Fix: location permission returns denied when user selects approximate - #1504
Open
HasanAlqaisi wants to merge 26 commits into
Open
HasanAlqaisi wants to merge 26 commits into
HasanAlqaisi wants to merge 26 commits into
Conversation
…ied when user selects approximate
|
I want this fix too. |
|
I can't reproduce the bug. What device are you using? What uses-permission you declared in AndroidManifest.xml? |
Author
I tried both Android 14 emulator & Android 16 (S25 FE) and they have the same issue. I made a simple repo for the bug reproduction |
|
need this fixed |
* feat(apple): create SPM source directory structure * feat(apple): move ObjC sources to SPM-compatible structure * feat(apple): add public header for SPM plugin registration * feat(apple): move PrivacyInfo.xcprivacy to SPM sources * feat(apple): add Package.swift for Swift Package Manager support * feat(apple): update podspec to reference SPM source structure * chore(apple): ignore SPM build artifacts * docs: add SPM setup instructions to README * fix(apple): move Package.swift to correct SPM location (ios/permission_handler_apple/) * fix(apple): add missing PERMISSION_PHOTOS_ADD_ONLY and PERMISSION_LOCATION_ALWAYS defines * fix(apple): use forward declaration in public header to avoid missing internal import * fix(example): bump compileSdk to 36, AGP to 8.9.1, Gradle to 8.11.1 * fix(apple): add explicit UIKit import to strategies that use UIApplication under SPM * feat(apple): auto-detect permissions from Info.plist in Package.swift Package.swift now walks up the directory tree from its own location to find the app's Info.plist and enables each permission define when the corresponding usage description key is present. This mirrors the CocoaPods workflow: adding NSCameraUsageDescription to Info.plist is all that is needed to activate PERMISSION_CAMERA, with no extra configuration files or terminal commands. Environment variables remain supported as an explicit override (priority over Info.plist), which covers PermissionGroup.notification and criticalAlerts that have no required Info.plist key. Users must clear DerivedData once after changing Info.plist so Xcode re-evaluates the manifest: rm -rf ~/Library/Developer/Xcode/DerivedData * docs: update SPM setup instructions to use Info.plist auto-detection Replace the launchctl setenv / pre-action script approach with the new Info.plist-based mechanism: permissions are now enabled automatically when the corresponding usage description key is present in Info.plist, which is already required for any permission to work at runtime. Document the two permissions without an Info.plist key (notification, criticalAlerts) as the only case still requiring an env var. * chore(example): update iOS example app and remove plan artifact - Add all permission usage description keys to Info.plist so the SPM Info.plist auto-detection covers all permissions out of the box - Comment out the Siri entitlement (requires a paid Apple Developer account; uncomment to test PERMISSION_ASSISTANT) - Update AppDelegate to modern FlutterImplicitEngineDelegate pattern - Bump Podfile iOS platform to 13.0 - Remove docs/superpowers/plans/2026-05-05-spm-support.md (internal planning artifact not intended for the public repo) * chore(apple): bump version to 9.4.8 and update CHANGELOG * fix(apple): correct SPM permission flag mapping for photos and calendarWriteOnly - PERMISSION_PHOTOS now triggers on NSPhotoLibraryAddUsageDescription alone, since PhotoPermissionStrategy (which handles photosAddOnly) compiles under PERMISSION_PHOTOS — without this, photosAddOnly silently fell back to UnknownPermissionStrategy when NSPhotoLibraryUsageDescription was absent - PERMISSION_EVENTS_FULL_ACCESS now also triggers on NSCalendarsWriteOnlyAccessUsageDescription (iOS 17+), enabling calendarWriteOnly which requires PERMISSION_EVENTS || PERMISSION_EVENTS_FULL_ACCESS in native code - Sync podspec version to 9.4.8 - Restore NSCameraUsageDescription in example Info.plist (lost during rewrite) - Add NSCalendarsWriteOnlyAccessUsageDescription to example Info.plist * docs: add calendarWriteOnly to SPM permission table in README * fix(example): remove NSSiriUsageDescription and document permission constraints Siri requires the com.apple.developer.siri entitlement; including NSSiriUsageDescription without it crashes the app on launch under SPM. Added a README section listing permissions that cannot be tested on simulator or without special entitlements. * fix(apple): enable notifications and criticalAlerts by default under SPM These permissions have no required Info.plist key so the previous logic always compiled them out (defaultValue "0"), causing permanentlyDenied to be returned without ever showing a system dialog. They are now enabled by default and can be opted out via env var set to "0". * fix(apple): revert criticalAlerts to opt-in under SPM criticalAlerts requires a special Apple entitlement; compiling it into every app by default would add dead code for apps that don't use it. Only PERMISSION_NOTIFICATIONS defaults to enabled (no entitlement needed). * docs: clarify SPM special cases for notification and criticalAlerts - Add both permissions to the Info.plist table with notes - Distinguish export (terminal) vs launchctl setenv (Xcode GUI) - Explain why criticalAlerts is opt-in (Apple entitlement required) * chore(example): enable SPM in Xcode project for iOS example app Flutter auto-generated FlutterGeneratedPluginSwiftPackage reference when running with --enable-swift-package-manager. * fix(example): align Java source/target compatibility to VERSION_17 AGP 8.x + Kotlin 1.9+ enforce JVM-target consistency; compileJava was still on 1.8 while compileKotlin used 17, causing the build to fail.
Fix incorrect Android SDK parameter name in documentation. "completeSdkVersion" is not a valid Android parameter. The correct name is "compileSdkVersion". This prevents developer confusion when following the troubleshooting guide.
…low#1488) After version 8.0.0, permission handler no longer uses all permissions on iOS, but this was not explained clearly in the README. This is my attempt to fix that small mistake.
> > Co-authored-by: Novarest
* Changed Info.plist lookup * Updated pubspec and changelog * Refactored look up of infoplist in the Package.swift * Reverting SPM changes in the example app. * fixed changelog.
…flow#1541) * Added ACCESS_LOCAL_NETWORK permission for android 17 * bump main package version * bump compilesdk version for android * Updated android pakcage version to 14 (breaking) * Just a typo * missing defs * ... * ... * Added ACCESS_LOCAL_NETWORK permission for android 17 * bump main package version * bump compilesdk version for android * Updated android pakcage version to 14 (breaking) * Just a typo * missing defs * ... * ... * dart formatting * Update to use monorepo for managing development * Linting, formatting, fix analysis issues * Fix test for number of permissions * Build after test (build is more time-costly) * Fix permission_handler android build compileSdk * Forgot to bump compilesdk version for android example app * update code coverage for permission_handler_platform_interface, add android 37 sdk * ...it is android 37.0, not android 37 * try to symlink the android-37.0 directory to android-37? * ohhh...i think it is flutter. * yes, flutter, but also outdated kotlin / gradle, etc * feat(apple): add Swift Package Manager support (Baseflow#1523) * feat(apple): create SPM source directory structure * feat(apple): move ObjC sources to SPM-compatible structure * feat(apple): add public header for SPM plugin registration * feat(apple): move PrivacyInfo.xcprivacy to SPM sources * feat(apple): add Package.swift for Swift Package Manager support * feat(apple): update podspec to reference SPM source structure * chore(apple): ignore SPM build artifacts * docs: add SPM setup instructions to README * fix(apple): move Package.swift to correct SPM location (ios/permission_handler_apple/) * fix(apple): add missing PERMISSION_PHOTOS_ADD_ONLY and PERMISSION_LOCATION_ALWAYS defines * fix(apple): use forward declaration in public header to avoid missing internal import * fix(example): bump compileSdk to 36, AGP to 8.9.1, Gradle to 8.11.1 * fix(apple): add explicit UIKit import to strategies that use UIApplication under SPM * feat(apple): auto-detect permissions from Info.plist in Package.swift Package.swift now walks up the directory tree from its own location to find the app's Info.plist and enables each permission define when the corresponding usage description key is present. This mirrors the CocoaPods workflow: adding NSCameraUsageDescription to Info.plist is all that is needed to activate PERMISSION_CAMERA, with no extra configuration files or terminal commands. Environment variables remain supported as an explicit override (priority over Info.plist), which covers PermissionGroup.notification and criticalAlerts that have no required Info.plist key. Users must clear DerivedData once after changing Info.plist so Xcode re-evaluates the manifest: rm -rf ~/Library/Developer/Xcode/DerivedData * docs: update SPM setup instructions to use Info.plist auto-detection Replace the launchctl setenv / pre-action script approach with the new Info.plist-based mechanism: permissions are now enabled automatically when the corresponding usage description key is present in Info.plist, which is already required for any permission to work at runtime. Document the two permissions without an Info.plist key (notification, criticalAlerts) as the only case still requiring an env var. * chore(example): update iOS example app and remove plan artifact - Add all permission usage description keys to Info.plist so the SPM Info.plist auto-detection covers all permissions out of the box - Comment out the Siri entitlement (requires a paid Apple Developer account; uncomment to test PERMISSION_ASSISTANT) - Update AppDelegate to modern FlutterImplicitEngineDelegate pattern - Bump Podfile iOS platform to 13.0 - Remove docs/superpowers/plans/2026-05-05-spm-support.md (internal planning artifact not intended for the public repo) * chore(apple): bump version to 9.4.8 and update CHANGELOG * fix(apple): correct SPM permission flag mapping for photos and calendarWriteOnly - PERMISSION_PHOTOS now triggers on NSPhotoLibraryAddUsageDescription alone, since PhotoPermissionStrategy (which handles photosAddOnly) compiles under PERMISSION_PHOTOS — without this, photosAddOnly silently fell back to UnknownPermissionStrategy when NSPhotoLibraryUsageDescription was absent - PERMISSION_EVENTS_FULL_ACCESS now also triggers on NSCalendarsWriteOnlyAccessUsageDescription (iOS 17+), enabling calendarWriteOnly which requires PERMISSION_EVENTS || PERMISSION_EVENTS_FULL_ACCESS in native code - Sync podspec version to 9.4.8 - Restore NSCameraUsageDescription in example Info.plist (lost during rewrite) - Add NSCalendarsWriteOnlyAccessUsageDescription to example Info.plist * docs: add calendarWriteOnly to SPM permission table in README * fix(example): remove NSSiriUsageDescription and document permission constraints Siri requires the com.apple.developer.siri entitlement; including NSSiriUsageDescription without it crashes the app on launch under SPM. Added a README section listing permissions that cannot be tested on simulator or without special entitlements. * fix(apple): enable notifications and criticalAlerts by default under SPM These permissions have no required Info.plist key so the previous logic always compiled them out (defaultValue "0"), causing permanentlyDenied to be returned without ever showing a system dialog. They are now enabled by default and can be opted out via env var set to "0". * fix(apple): revert criticalAlerts to opt-in under SPM criticalAlerts requires a special Apple entitlement; compiling it into every app by default would add dead code for apps that don't use it. Only PERMISSION_NOTIFICATIONS defaults to enabled (no entitlement needed). * docs: clarify SPM special cases for notification and criticalAlerts - Add both permissions to the Info.plist table with notes - Distinguish export (terminal) vs launchctl setenv (Xcode GUI) - Explain why criticalAlerts is opt-in (Apple entitlement required) * chore(example): enable SPM in Xcode project for iOS example app Flutter auto-generated FlutterGeneratedPluginSwiftPackage reference when running with --enable-swift-package-manager. * fix(example): align Java source/target compatibility to VERSION_17 AGP 8.x + Kotlin 1.9+ enforce JVM-target consistency; compileJava was still on 1.8 while compileKotlin used 17, causing the build to fail. * Update version to 12.0.2 * fix typo in README about the compileSdkVersion (Baseflow#1472) * docs: fix completeSdkVersion typo to compileSdkVersion (Baseflow#1494) Fix incorrect Android SDK parameter name in documentation. "completeSdkVersion" is not a valid Android parameter. The correct name is "compileSdkVersion". This prevents developer confusion when following the troubleshooting guide. * Improved the iOS Setup section in permission_handler/README.md (Baseflow#1488) After version 8.0.0, permission handler no longer uses all permissions on iOS, but this was not explained clearly in the README. This is my attempt to fix that small mistake. * Rewrite copyleft code from stackoverflow > > Co-authored-by: Novarest * Bump permission_handler_apple to 9.4.9 * Bump permission_handler to 12.0.3 * Bump permission_handler_apple to 9.4.9 * Added ACCESS_LOCAL_NETWORK permission for android 17 * bump main package version * missing defs * dart formatting * Update to use monorepo for managing development * Linting, formatting, fix analysis issues * Fix test for number of permissions * Build after test (build is more time-costly) * Fix permission_handler android build compileSdk * Forgot to bump compilesdk version for android example app * update code coverage for permission_handler_platform_interface, add android 37 sdk * ...it is android 37.0, not android 37 * try to symlink the android-37.0 directory to android-37? * ohhh...i think it is flutter. * yes, flutter, but also outdated kotlin / gradle, etc * accidental extra case during merge * updated gitignore for ios * Updated android plugin gradle / config * remove ios ephemeral * remove main permission_handler example ios ephemeral --------- Co-authored-by: Gauhar <asanbayg@gmail.com> Co-authored-by: harshit saini <harshitsaini666@gmail.com> Co-authored-by: AlHomam Sultan <Hemobs24@gmail.com> Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
Baseflow#1553) The Package.swift manifest derives the PERMISSION_* macros from the host app's Info.plist. Discovery failed in two independent ways: - It only ever looked at `ios/Runner/Info.plist`, so apps using build configuration or flavor specific plists (`Info-Debug.plist`, `Runner/Info-$(CONFIGURATION).plist`, ...) were never matched. - The app-root lookup required a pubspec.yaml and a loadable plist in the same condition, so a miss did not stop the walk-up at the app and could adopt an unrelated app's plist in a monorepo. Info.plist locations are now resolved from INFOPLIST_FILE in the Xcode project and any .xcconfig files, with a scan of `ios/` as a fallback, and the usage description keys found across them are merged. The app root is anchored on a pubspec.yaml next to an `ios/*.xcodeproj`, which is what distinguishes a host app from a plugin package. The INFOPLIST_FILE pattern is anchored so it cannot match inside GENERATE_INFOPLIST_FILE, which Xcode writes into every target it creates from a template, and candidates that are not files on disk no longer count towards "the build settings produced something". Either one alone was enough to suppress the fallback scan and compile out every permission, for an app whose own target names its plist through a build variable this manifest does not expand. Adds PERMISSION_HANDLER_INFO_PLIST to point the manifest at specific plists, which is the only mechanism available to builds started from Xcode.app: those run with `/` as their working directory, and Xcode passes none of its build settings to manifest evaluation. Adds PERMISSION_HANDLER_VERBOSE to log the app root, the plists used and the resolved macros, because Xcode discards manifest output entirely. The example app's Debug configuration now uses a separate `Info-Debug.plist` to cover the reported layout. It is identical to Info.plist except that it declares no NSContactsUsageDescription, so PERMISSION_CONTACTS can only resolve to 1 by merging the two, which is what lets CI assert on the discovery rather than on the example's default plist. A second CI step builds a project that forces the scan fallback. Fixes Baseflow#1548
…/2) (Baseflow#1554) * Add opt-in per-flavor permissions for Swift Package Manager builds Permissions were merged across every Info.plist the manifest could find, so an app whose dev flavor declares camera access compiled the camera code into its prod binary too — the ITMS-90683 rejection the macros exist to avoid. An app can now declare a permission_handler.json next to its pubspec.yaml, mapping each flavor to the single Info.plist that defines it. Only the selected flavor's plist is read and nothing is merged, so a flavor cannot inherit another flavor's permissions. Apps without the file keep the previous behaviour. This is a Swift Package Manager feature. CocoaPods builds take the PERMISSION_* macros from the Podfile, where they can already be set per configuration, so permission_handler.json does not apply to them and the verification build phase exits early when the SPM package is not part of the build. Without that the example, which builds under CocoaPods, would require a flavor selection that changes nothing. Selecting a flavor is a separate step rather than something the manifest derives, because of three properties of Swift package manifests that were measured rather than assumed: - Xcode passes none of its build settings to manifest evaluation, so CONFIGURATION is not available and the manifest cannot know which flavor is building. - Xcode does not re-evaluate a cached manifest when an environment variable or the selection changes. It keeps serving the previous answer until SourcePackages, XCBuildData and the shared SwiftPM manifest cache are gone. - Scheme pre-actions run after package resolution, so they cannot prepare the selection for the build that follows. `dart run permission_handler_apple:select <flavor>` therefore records the choice and clears exactly those caches. Since a cached manifest is never executed, nothing inside it can detect that its own result went stale. tool/verify_flavor_selection.sh runs as a build phase on the app target, where CONFIGURATION is available, and fails the build when the selected flavor does not match the configuration being built. It runs first in the phase list, so a mismatch costs a second rather than a full compile, and it resolves the plugin through either the SPM package or .symlinks so the same snippet works whichever way a project consumes it. A flavor whose infoPlist does not exist is an error rather than a fallback to discovery: falling back would hand that flavor the union of every other flavor's permissions, which is the leak this exists to stop. The example declares two flavors over the two Info.plist files that differ by a single key. CI asserts that neither flavor sees the other's permission, that a missing selection compiles nothing in under "strict": true, that a mismatched selection fails the build, and — with the config switched off — that the merging path this replaces still resolves both plists. The repository .gitignore excluded every bin/ directory, which would have dropped the new executable from the package; pub requires it to live there. * Use YAML for the per-flavor permission configuration Flutter developers configure everything else in YAML, so the per-flavor config moves from permission_handler.json to permission_handler.yaml, as requested in review. None of the build-time consumers can parse YAML natively: a Swift package manifest cannot import libraries for its own evaluation and Foundation has no YAML support, and the verification build phase runs on python3 whose standard library has none either. Rather than hand-rolling a YAML parser in each of them, the `select` command becomes the single YAML reader — it parses the config with package:yaml, the same parser pub itself uses, and translates it into a generated ios/Flutter/permission_handler.resolved.json that the manifest and the build phase keep reading with the native JSON parsers they already had. The generated file is an internal artifact: gitignored, never edited, regenerated on every `select`. Two guards keep it from going stale. The manifest and the build phase both refuse to build when the YAML is newer than its translation or when the translation is missing, compiling no permissions in and saying to re-run `select` — falling back to discovery instead would hand the build the union of every flavor's permissions, the exact leak the config exists to prevent. `select` was already mandatory before any flavored build because Xcode never re-evaluates a cached manifest, so the translation step adds no new workflow. CI gains a staleness assertion: touching permission_handler.yaml without re-running `select` must compile everything out and name the guard in the diagnostics. * Reject ambiguous flavor configurations instead of guessing Two flavors could list the same build configuration. Nothing rejected that, and the verification build phase resolved it by taking whichever came first, so selecting the flavor that configuration actually belongs to failed the build with a message naming the other one — talking the user into shipping the wrong flavor's permissions, which is the leak the config exists to prevent. A `configurations` written as a scalar rather than a list became an empty list, silently removing every configuration of that flavor from the verification phase's view. The build then reported that it could not verify the permissions and continued. `select` is the only reader of the YAML, so it is the only place where either can be reported at all: everything downstream sees the generated JSON, where a typo and a deliberate value are indistinguishable. It now refuses a configuration claimed by more than one flavor, a `configurations` that is not a list, a non-string flavor name, and a `strict` that is not a boolean. Three guards that failed open now fail closed. The manifest treated unreadable modification times as "not stale" and built anyway; the verification phase read a failing `find` as "not stale" for the same reason; and PERMISSION_HANDLER_CONFIG was honoured only for locating the YAML, while its generated translation and Info.plists were still resolved against the automatically detected app — so the file that gated the staleness check and the file that supplied the permissions could come from two different apps.
…Baseflow#1555) The "iOS - Swift Package Manager" section of permission_handler/README.md is where most users will look, but it no longer covers what the package does. This brings it up to date and points to the permission_handler_apple README for the full detail, rather than duplicating documentation that would drift. Follow-up to Baseflow#1554, as discussed there. The Info.plist key table was wrong in four places, checked against the plistKeys of each macro in Package.swift: calendarWriteOnly and calendarFullAccess were listed as two separate rows when both map to the same macro (PERMISSION_EVENTS_FULL_ACCESS, either key enables both); photos, location and bluetooth were each missing a second key they also accept; and locationWhenInUse shared a row with location despite being a narrower, separate macro. The section also claimed "no additional configuration file is needed", which stopped being true with Baseflow#1554. Separately, PERMISSION_HANDLER_VERBOSE was documented in both this README and permission_handler_apple/README.md as always logging "the app root". That only holds without a permission_handler.yaml; with one, it logs the active flavor instead. Fixed in both places. permission_handler_apple/README.md is the README this section now points users to, so a few gaps found in the same pass are closed there too: strict: false was never explained (only strict: true was), select's validation added in Baseflow#1554 had one line covering one of its four rules, and select --app / --derived-data were undocumented despite being what makes the command usable in CI. Adds to permission_handler/README.md: how Info.plist files are located and merged across build configurations, a short flavors example pointing to the full per-flavor documentation, the Xcode.app case, and a troubleshooting block for the silent-denied failure mode behind Baseflow#1548. No code changes, so no version bump and no CHANGELOG.md entry.
* Fix Android status reporting permanentlyDenied after "Ask every time" On Android 11+, selecting "Ask every time" in the app settings revokes the permission as a one-time permission, which clears FLAG_PERMISSION_USER_SET. shouldShowRequestPermissionRationale() then returns false, exactly as for a permission that was never requested or one that is permanently denied. The plugin combined that with a "was denied before" flag in SharedPreferences that was never cleared, so `status` kept reporting permanentlyDenied even though the OS would show the request dialog again (Baseflow#1206). - A status check now always reports `denied` for a denied runtime permission; Android offers no API to tell the three states apart. - `request()` resolves permanentlyDenied from the change of the rationale flag across the request (true -> false means the second denial), falling back to the stored flag when the OS resolved the request without a dialog. - The stored flag is cleared whenever the permission is observed granted. - Docs, README and changelogs updated; versions bumped. Verified on an Android 16 emulator with the example app: the old build reports permanentlyDenied after "Ask every time", the fixed build reports denied and shows the dialog on the next request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Add app adoption guide for the Android permanently-denied fix Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Add app adoption guide for the Android permanently-denied fix Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…w#1560) The iOS CocoaPods setup mixed the calendar Podfile example with a camera plist key, and "corresponding" was easy to read as pointing at step 2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue: On Android 12 (API 31) and higher, users have the option to grant "Approximate" location (Coarse) instead of "Precise" location (Fine). When a user selects "Approximate", the system grants ACCESS_COARSE_LOCATION but denies ACCESS_FINE_LOCATION.
Currently, permission_handler incorrectly reports the status as PermissionStatus.denied in this scenario. This prevents apps from functioning that are capable of working with only approximate location.
It is also reported as an issue here
Expected Behavior: If the user grants "Approximate" location, the permission status should be reported as PermissionStatus.granted, as the app does have access to location data (with lower accuracy).
Pre-launch Checklist
pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or this PR is does not need version changes.CHANGELOG.mdto add a description of the change.///).main.dart format .and committed any changes.flutter analyzeand fixed any errors.