
Simplifies platform permission flows by exposing the real state machine, stateless-by-default with optional BYO storage, sealed permission model, crash-safe API, Compose-native controller and DI-agnostic integrations.
आज्ञा, Sanskrit for permission, consent.
A small, opinionated Kotlin Multiplatform permission library for Android and iOS.
Built so you stop reimplementing the same shouldShowRequestPermissionRationale /
CLAuthorizationStatus dance in every app.
Permissions on Android and iOS are not the same shape:
Most libraries paper over this with the lowest common denominator (one ask, ever) and force you to pick a storage strategy. Aagya does neither.
It exposes the real state machine:
NotDetermined
becomes Granted (user allows)
becomes Denied(canAskAgain = true) (request again)
becomes Denied(canAskAgain = false) (open Settings)
…and lets you decide if you want to layer a stricter app policy on top (with your storage of choice).
shouldShowRequestPermissionRationale on Android and CLAuthorizationStatus on iOS as the source of truth. Zero storage required for the common case.:store-datastore (Android) and :store-userdefaults (iOS) adapters ship in the box.rememberPermissionController() for Compose users; plain interface for non-Compose KMP code.:di-koin module if you use Koin. Otherwise, build it yourself with rememberPermissionController().// build.gradle.kts (KMP shared module)
kotlin {
sourceSets {
commonMain.dependencies {
implementation("xyz.ksharma:aagya-data:0.3.0")
}
}
}Upgrading from 0.2.0 or earlier? The group ID changed in 0.3.0, from
io.github.ksharma-xyztoxyz.ksharma, matching thexyz.ksharma.*package names the library has always shipped. Maven Central coordinates cannot be moved, so 0.2.0 and earlier remain under the old group. Change the group when you upgrade; artifact names and the API are unchanged.
@Composable
fun LocationButton() {
val controller = rememberPermissionController()
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
when (val result = controller.requestPermission(AppPermission.Location.Fine)) {
is PermissionResult.Granted -> startLocationFlow()
is PermissionResult.Denied -> if (!result.canAskAgain) controller.openAppSettings()
is PermissionResult.Cancelled -> Unit
}
}
}) {
Text("Use my location")
}
}That's it. No singletons, no Application subclass to extend, no storage to set up.
val controller = rememberPermissionController(
policy = PermissionPolicy(
maxRequestsAndroid = 1,
maxRequestsIos = 1,
store = rememberDataStorePermissionStore(),
),
)See recipes for stricter policies, custom storage, and DI integration.
| Artifact | Purpose | Required? |
|---|---|---|
xyz.ksharma:aagya-state |
Pure-Kotlin types: AppPermission, PermissionStatus, PermissionResult, PermissionPolicy. |
yes |
xyz.ksharma:aagya-data |
PermissionController interface and Android/iOS implementations. |
yes |
xyz.ksharma:aagya-store-datastore |
Android PermissionStore backed by Jetpack DataStore. |
optional |
xyz.ksharma:aagya-store-userdefaults |
iOS PermissionStore backed by NSUserDefaults. |
optional |
xyz.ksharma:aagya-di-koin |
Koin module factory if you use Koin. | optional |
iosArm64, iosSimulatorArm64)JVM and JS targets may follow if there is real demand. Open an issue.
v0.1 shipped Location.Fine and Location.Coarse; v0.2 added Microphone.Default.
The sealed hierarchy already includes stubs for the rest so adding them is
non-breaking:
CameraNotificationsPhotos (read / read-write split for iOS 14+)Calendar, Contacts
See CONTRIBUTING.md. PRs welcome. Please open an issue before any non-trivial work so we can agree on the shape of the API.
Apache 2.0. Use it however you want.
Aagya is named for आज्ञा, the Sanskrit word for permission / command. The library gives you a clean way to ask the user for theirs.
आज्ञा, Sanskrit for permission, consent.
A small, opinionated Kotlin Multiplatform permission library for Android and iOS.
Built so you stop reimplementing the same shouldShowRequestPermissionRationale /
CLAuthorizationStatus dance in every app.
Permissions on Android and iOS are not the same shape:
Most libraries paper over this with the lowest common denominator (one ask, ever) and force you to pick a storage strategy. Aagya does neither.
It exposes the real state machine:
NotDetermined
becomes Granted (user allows)
becomes Denied(canAskAgain = true) (request again)
becomes Denied(canAskAgain = false) (open Settings)
…and lets you decide if you want to layer a stricter app policy on top (with your storage of choice).
shouldShowRequestPermissionRationale on Android and CLAuthorizationStatus on iOS as the source of truth. Zero storage required for the common case.:store-datastore (Android) and :store-userdefaults (iOS) adapters ship in the box.rememberPermissionController() for Compose users; plain interface for non-Compose KMP code.:di-koin module if you use Koin. Otherwise, build it yourself with rememberPermissionController().// build.gradle.kts (KMP shared module)
kotlin {
sourceSets {
commonMain.dependencies {
implementation("xyz.ksharma:aagya-data:0.3.0")
}
}
}Upgrading from 0.2.0 or earlier? The group ID changed in 0.3.0, from
io.github.ksharma-xyztoxyz.ksharma, matching thexyz.ksharma.*package names the library has always shipped. Maven Central coordinates cannot be moved, so 0.2.0 and earlier remain under the old group. Change the group when you upgrade; artifact names and the API are unchanged.
@Composable
fun LocationButton() {
val controller = rememberPermissionController()
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
when (val result = controller.requestPermission(AppPermission.Location.Fine)) {
is PermissionResult.Granted -> startLocationFlow()
is PermissionResult.Denied -> if (!result.canAskAgain) controller.openAppSettings()
is PermissionResult.Cancelled -> Unit
}
}
}) {
Text("Use my location")
}
}That's it. No singletons, no Application subclass to extend, no storage to set up.
val controller = rememberPermissionController(
policy = PermissionPolicy(
maxRequestsAndroid = 1,
maxRequestsIos = 1,
store = rememberDataStorePermissionStore(),
),
)See recipes for stricter policies, custom storage, and DI integration.
| Artifact | Purpose | Required? |
|---|---|---|
xyz.ksharma:aagya-state |
Pure-Kotlin types: AppPermission, PermissionStatus, PermissionResult, PermissionPolicy. |
yes |
xyz.ksharma:aagya-data |
PermissionController interface and Android/iOS implementations. |
yes |
xyz.ksharma:aagya-store-datastore |
Android PermissionStore backed by Jetpack DataStore. |
optional |
xyz.ksharma:aagya-store-userdefaults |
iOS PermissionStore backed by NSUserDefaults. |
optional |
xyz.ksharma:aagya-di-koin |
Koin module factory if you use Koin. | optional |
iosArm64, iosSimulatorArm64)JVM and JS targets may follow if there is real demand. Open an issue.
v0.1 shipped Location.Fine and Location.Coarse; v0.2 added Microphone.Default.
The sealed hierarchy already includes stubs for the rest so adding them is
non-breaking:
CameraNotificationsPhotos (read / read-write split for iOS 14+)Calendar, Contacts
See CONTRIBUTING.md. PRs welcome. Please open an issue before any non-trivial work so we can agree on the shape of the API.
Apache 2.0. Use it however you want.
Aagya is named for आज्ञा, the Sanskrit word for permission / command. The library gives you a clean way to ask the user for theirs.