Irbisa · Cheatsheet
Permissions & Privacy
Runtime permissions, purpose strings, ATT, location, photos, privacy manifests, consent, SDK audit
How does requesting a permission differ between Android and iOS?
What do "Only this time" and "Approximate" in the Android permission dialog mean for your code?
Onboarding asks for location, contacts and notifications on the first screen, and most users deny all three. When should you ask instead?
@Composablefun AddToCalendarButton(vm: BookingViewModel) { val context = LocalContext.current var showPrimer by remember { mutableStateOf(false) } val launcher = rememberLauncherForActivityResult( ActivityResultContracts.RequestPermission()…The app crashes when the user taps Scan. What is wrong with this permission request?
class ScanFragment : Fragment(R.layout.fragment_scan) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.findViewById<Button>(R.id.scan).setOnClickListener { val request = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { granted -> if (granted) startScanner() } request.launch(Manifest.permission.CAMERA) } }}shouldShowRequestPermissionRationalereturnsfalseboth on a fresh install and after the user blocked the permission. How do you tell these states apart?class PermissionGate(private val prefs: DataStore<Preferences>) { private fun blockedKey(permission: String) = booleanPreferencesKey("blocked_$permission") suspend fun state(activity: Activity, permission: String): PermissionState = when { ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED -> PermissionState.Granted…Which features can you ship without requesting a dangerous permission at all, by letting a system component do the work?
// Photo: the camera app takes it and writes to your FileProvider Uri. No CAMERA permission.private val takePhoto = registerForActivityResult(ActivityResultContracts.TakePicture()) { saved -> if (saved) upload(photoUri)}// takePhoto.launch(photoUri)…What is the Android Photo Picker and partial media access in Android 14, and why prefer the picker over
READ_MEDIA_IMAGES?// Attaching an avatar: no permission, no manifest entry, nothing to denyval pickAvatar = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> if (uri != null) upload(uri)}fun chooseAvatar() = pickAvatar.launch( PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)…Find the bug: the avatar the user picked shows fine, but after the app is restarted it is gone and the log shows an exception.
class ProfileFragment : Fragment(R.layout.fragment_profile) { private val prefs by lazy { requireContext().getSharedPreferences("profile", Context.MODE_PRIVATE) } private val pickAvatar = registerForActivityResult(PickVisualMedia()) { uri -> if (uri == null) return@registerForActivityResult prefs.edit { putString("avatar_uri", uri.toString()) } showAvatar(uri) // reads it with contentResolver.openInputStream(uri) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { prefs.getString("avatar_uri", null)?.let { showAvatar(it.toUri()) } view.findViewById<Button>(R.id.change_avatar).setOnClickListener { pickAvatar.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly)) } }}A courier app needs the driver's location while the screen is off during a delivery. Which permissions do you request, and do you need background location at all?
private val requestLocation = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions()) { result -> when { result[Manifest.permission.ACCESS_FINE_LOCATION] == true -> startTrip(precise = true) result[Manifest.permission.ACCESS_COARSE_LOCATION] == true -> startTrip(precise = false)…After raising
targetSdkthis always shows "No maps app installed", even with Google Maps on the phone. Why, and how do you fix it?fun openInMaps(context: Context, lat: Double, lng: Double) { val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:$lat,$lng")) if (intent.resolveActivity(context.packageManager) != null) { context.startActivity(intent) } else { Toast.makeText(context, "No maps app installed", Toast.LENGTH_SHORT).show() }}What do the Android 12 camera and microphone indicators and toggles change for your app?
A minor SDK update adds
READ_PHONE_STATEto your release manifest. How do you find where it came from, and what are your options?// build.gradle.kts (:app): run ./gradlew checkReleasePermissions in CI// import com.android.build.api.artifact.SingleArtifactabstract class CheckPermissions : DefaultTask() { @get:InputFile abstract val mergedManifest: RegularFileProperty @get:InputFile abstract val allowList: RegularFileProperty…What does the Play Data safety form require, and how does a third-party SDK change your answers?
What is App Tracking Transparency, and what happens to the IDFA and ad attribution when the user declines?
import AppTrackingTransparencyimport AdSupport // Call when the first screen is visible, e.g. when scenePhase becomes .active.// Called from didFinishLaunching, the alert is never shown.@MainActor…Your iOS upload is rejected with ITMS-91053 for an SDK you did not write. What does the privacy manifest declare, and what is Apple checking?
<plist version="1.0"><dict> <key>NSPrivacyTracking</key> <false/> <key>NSPrivacyTrackingDomains</key> <array/>…Every analytics call sits behind a consent flag, yet the vendor is contacted before the consent dialog is answered. Where does that request come from?
// AndroidManifest.xml: read before any of your code runs// <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />// <meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />// <meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="false" /> data class Consent(val analytics: Boolean, val ads: Boolean, val crashes: Boolean)…Product wants to open the app to children under 13. What changes in the store declarations, the ad and analytics stack, and the data you may collect?
enum class Audience { CHILD, TEEN, ADULT } // Applied once, before MobileAds.initialize()// AndroidManifest.xml: <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />fun configureForAudience(app: Application, audience: Audience) { val child = audience == Audience.CHILD…A user taps "Delete my account". What has to happen beyond deleting the user row, and what do you promise about backups?
A reviewer asks you to prove the app sends only what your Data safety form claims. What evidence can you produce?