Permissions
27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.
npx -y skills add piyushverma0/android-agent-skills --skill permissionsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Android runtime permissions for AI agents. Use this skill whenever requesting permissions, handling permission results, camera permission, location permission, microphone permission, storage permission, contacts permission, notifications permission, Bluetooth permission, ActivityResultLauncher, requestPermissions, shouldShowRequestPermissionRationale, Accompanist Permissions, rememberPermissionState, rememberMultiplePermissionsState, permission denied handling, permission rationale dialog, or any runtime permission flow.
SKILL.md
6.0 KB, as published. Nobody here has run it
Android Runtime Permissions
Rule 1: Always use Accompanist Permissions in Compose
implementation(libs.accompanist.permissions)
// ✅ Single permission
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
val cameraPermission = rememberPermissionState(Manifest.permission.CAMERA)
when {
cameraPermission.status.isGranted -> {
CameraPreview()
}
cameraPermission.status.shouldShowRationale -> {
PermissionRationale(
title = "Camera required",
description = "This feature requires camera access to scan items.",
onRequest = { cameraPermission.launchPermissionRequest() }
)
}
else -> {
// First time or permanently denied
LaunchedEffect(Unit) { cameraPermission.launchPermissionRequest() }
}
}
}
// ✅ Multiple permissions
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationScreen() {
val locationPermissions = rememberMultiplePermissionsState(
listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
when {
locationPermissions.allPermissionsGranted -> LocationContent()
locationPermissions.shouldShowRationale -> PermissionRationale(
title = "Location required",
description = "We need your location to show nearby items.",
onRequest = { locationPermissions.launchMultiplePermissionRequest() }
)
else -> LaunchedEffect(Unit) { locationPermissions.launchMultiplePermissionRequest() }
}
}
Rule 2: Rationale dialog — always explain before requesting
// ✅ Standard permission rationale composable
@Composable
fun PermissionRationale(
title: String,
description: String,
onRequest: () -> Unit,
onDismiss: () -> Unit = {},
onOpenSettings: (() -> Unit)? = null
) {
AlertDialog(
onDismissRequest = onDismiss,
icon = { Icon(Icons.Default.Info, contentDescription = null) },
title = { Text(title) },
text = { Text(description) },
confirmButton = {
TextButton(onClick = onRequest) { Text("Grant permission") }
},
dismissButton = {
if (onOpenSettings != null) {
TextButton(onClick = onOpenSettings) { Text("Open settings") }
} else {
TextButton(onClick = onDismiss) { Text("Not now") }
}
}
)
}
// ✅ Open app settings when permanently denied
fun Context.openAppSettings() {
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
})
}
Rule 3: Declare permissions in AndroidManifest
<!-- Required in AndroidManifest.xml before requesting -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Storage — Android 13+ uses granular permissions -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Android 12 and below -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<!-- Notifications — required for Android 13+ -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Bluetooth — Android 12+ split permissions -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Rule 4: Background location — separate request flow
// ✅ Request foreground location first, then background separately
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun BackgroundLocationScreen() {
val foregroundLocation = rememberMultiplePermissionsState(
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
)
val backgroundLocation = rememberPermissionState(
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
when {
!foregroundLocation.allPermissionsGranted -> {
// Step 1: request foreground first
RequestForegroundLocation(onRequest = { foregroundLocation.launchMultiplePermissionRequest() })
}
!backgroundLocation.status.isGranted -> {
// Step 2: only after foreground granted
RequestBackgroundLocation(onRequest = { backgroundLocation.launchPermissionRequest() })
}
else -> BackgroundLocationContent()
}
}
Common Mistakes
❌ Requesting permission without rationale — users deny without explanation ❌ Requesting multiple unrelated permissions at once — request only when needed ❌ Not handling permanently denied state — always offer "Open Settings" option ❌ Requesting background location before foreground — Android rejects this ❌ Missing permission in AndroidManifest — request will always be denied ❌ Requesting WRITE_EXTERNAL_STORAGE on Android 10+ — not needed, use scoped storage