agentsclimarketplace

Permissions

Skill piyushverma0/android-agent-skills/skills/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.

Install
npx -y skills add piyushverma0/android-agent-skills --skill permissions

Assembled 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

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.