Skip to content

Platform setup

Beekon declares the permissions it needs in its own manifest/Info.plist and merges them into your app at build time — but you still request the dangerous permissions at runtime and provide the OS-level declarations. Pick your platform; the tabs stay synced.

The install + first-fix flow is on the Quickstart; this page is the OS plumbing.

The .aar declares its permissions in its own manifest; they merge into your app at build. You request them at runtime and provide a NotificationConfig for the foreground service.

app/build.gradle.kts
android {
defaultConfig {
minSdk = 26 // Beekon's minimum
}
}
dependencies {
implementation("io.github.beekonlabs:beekon:0.4.0")
}

JDK 17 to build. explicitApi() is enforced — Kotlin 2.x is recommended but your app’s version needn’t match.

Add these to app/src/main/AndroidManifest.xml if you want to see them in your merged manifest:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

FOREGROUND_SERVICE_LOCATION is required from Android 14 (API 34). POST_NOTIFICATIONS is required from Android 13 (API 33) for the foreground-service notification to be visible.

Android forces a three-step sequence — you can’t ask for background location without first having foreground location, and notifications need their own grant on Android 13+.

  1. Request ACCESS_FINE_LOCATION + ACCESS_COARSE_LOCATION together (single dialog).
  2. On API 29+, request ACCESS_BACKGROUND_LOCATION separately. This always opens Settings on API 30+.
  3. On API 33+, request POST_NOTIFICATIONS.

The SDK’s requestPermission(...) / requestNextNeededPermission(...) perform this sequence correctly — pairing FINE + COARSE, requesting background only after foreground, and never firing a prompt the OS would silently swallow. If you’d rather drive it yourself, the Compose sample at beekon-android/sample ships a LocationPermissions composable that walks through this exact sequence — copy from there if you don’t have a permissions helper.

BeekonConfig.notification is required — Android shows a notification while location is captured in the background. Its three fields are documented on Configuration. The channel id and notification id are SDK-internal constants.

A license key is optional and purely observational — an absent key just means Beekon runs in evaluation mode, fully functional. If you’ve been issued a production token, supply it in configure(...) and read the status; it never blocks tracking. See Licensing for the full flow.

You request the OS permissions — but which ones depends on your config. getRequiredPermissions() answers that: it returns the exact set the current config needs, each tagged required or recommended and marked whether the live OS grant already covers it. Call it after configure(...) so the result reflects the active gate; before that it returns a conservative location-only list. In Cloud mode the config is server-owned and unknown pre-start, so it returns the same conservative list.

It’s read-only and never prompts — it tells you what’s needed; Requesting permissions below is how you ask. Each entry is a PermissionRequirement:

FieldTypeMeaning
permissionenumlocation, backgroundLocation, activityRecognition, or notifications (normalized, not raw OS constants).
importanceenumrequired (tracking can’t start, or an enabled feature is dead without it) or recommended (a feature silently degrades; tracking continues).
satisfiedboolWhether the live OS grant/capability already covers it.
rationalestringHuman-readable: why it’s needed / what degrades without it.

location and backgroundLocation are always present; activityRecognition appears when the config can use motion detection; notifications appears only on Android 13+ (it backs the foreground-service notification and per-geofence notifications) and never on iOS. Platform asymmetry on satisfied: on Android it’s the runtime grant; on iOS — which has no runtime motion-permission API — activityRecognition reflects the NSMotionUsageDescription plist key plus device capability.

// synchronous; call after configure()
Beekon.getRequiredPermissions().forEach { r ->
Log.d("beekon", "${r.permission} (${r.importance}) satisfied=${r.satisfied}: ${r.rationale}")
}
// r.permission: BeekonPermission.Location / .BackgroundLocation / .ActivityRecognition / .Notifications
// r.importance: PermissionImportance.Required / .Recommended

The doctor tells you what’s needed. This is how you ask — the explicit, app-initiated request layer that performs each OS permission ask correctly on each platform, exactly once per app decision to ask.

The policy is unchanged: Beekon never prompts implicitly. Nothing in configure(...), start(), or init ever raises a dialog, and there is no auto-firing chain — every prompt is one your code deliberately triggers, in context, behind your own rationale UI. requestPermission(...) is that explicit ask; it complements the read-only doctor rather than replacing it. Third-party permission libraries (permission_handler, react-native-permissions, …) remain fully supported — drive them off getRequiredPermissions() instead if you prefer.

Three methods:

MethodWhat it does
requestPermission(permission)Request one permission. Resolves to a PermissionRequestResult. Performs the platform-correct ask (see the platform notes below) and never fires a prompt the OS would silently swallow.
requestNextNeededPermission()Derives the doctor list and requests the first unsatisfied required permission in canonical order, then returns its result — or null/nil when nothing is needed. The idiomatic one call per onboarding screen. It walks required entries only; recommended ones stay deliberate app choices you surface from the doctor list.
openSettings(for: permission)Opens the closest reachable OS settings screen — the only path once an outcome is requiresSettings.

There’s no auto-chain by design: requestNextNeededPermission() advances one step per call. Ask, show your rationale for the next one, ask again on the next tap — one prompt per screen.

Read getRequiredPermissions() to render what’s needed and why, then call requestNextNeededPermission() in-context to ask for the first thing still missing. Repeat per screen until it returns null/nil, then start().

// suspend; needs a foreground Activity. Call from a coroutine.
when (val r = Beekon.requestNextNeededPermission(activity)) {
null -> Beekon.start() // nothing left to ask — good to go
else -> when (r.outcome) {
PermissionRequestOutcome.RequiresSettings ->
Beekon.openSettings(activity, BeekonPermission.Location)
else -> { /* re-render your rationale; ask again on the next tap */ }
}
}
// Or request one explicitly:
val res = Beekon.requestPermission(activity, BeekonPermission.BackgroundLocation)
// res.outcome, res.prerequisite (when Blocked), res.reason (when NotAttempted), res.status

Every request resolves to a flat PermissionRequestResult: an outcome, an optional prerequisite (a BeekonPermission, set only when outcome == blocked), an optional reason string (set only when outcome == notAttempted), and a refreshed PermissionStatus snapshot.

outcomeMeaning
grantedThe grant now covers the request. Includes iOS provisional Always and one-time grants — neither platform exposes durability, so it’s documented as unknowable.
deniedThe system prompt ran; the grant is still absent; a future prompt may remain possible.
requiresSettingsA further prompt is impossible — openSettings(...) is the only path left.
alreadySatisfiedNo-op; the grant already covered it.
blockedA precondition guard fired; prerequisite names the permission to request first (e.g. background location before foreground).
notAttemptedThe SDK declined to fire a doomed prompt; reason says why — missing_manifest_entry, missing_plist_key, unsupported, or not_foreground.

PermissionRequestOutcome is a host-facing enum and may gain values in a minor release — switch with an else/default branch.

  • Requests need a foreground Activity — off the foreground you get notAttempted with reason not_foreground.
  • The permission must be declared in the merged manifest or the request resolves notAttempted(missing_manifest_entry). Notably ACCESS_BACKGROUND_LOCATION is not in Beekon’s library manifest — your app must add it (see Permissions above).
  • Location requests pair FINE + COARSE in a single ask automatically.
  • Background must come after foreground. A background-location request while foreground location is still ungranted returns outcome = blocked, prerequisite = location (Android 11+ silently denies a combined ask with no UI). Request foreground first, then background.
  • notifications is runtime-requestable only on Android 13+ (API 33) targeting SDK 33+; otherwise notAttempted(unsupported).