barcode-scanner icon indicating copy to clipboard operation
barcode-scanner copied to clipboard

Android 11 permission "Ask every time" doesn't prompt the user to give camera permission again

Open denisLoncaric opened this issue 3 years ago • 7 comments

Describe the bug On android 11 there is a possibility to set the camera permission to "Ask every time". In case this permission is set plugins checkPermission method returns status denied set to true, only if user sets the permission manually to "Allow only while using the app" in Settings -> Apps -> [app] it is possible to scan the codes again. The issue appears after the device goes to sleep mode.

To Reproduce Steps to reproduce the behavior:

  1. Call the checkPermission
  2. On prompt select "Only this time"
  3. Code scanning works as expected
  4. Close the app and leave device until it goes to sleep mode or turn it off
  5. Open cell phone, open the app and call the checkPermission again (click on scann button)
  6. Result object has property denied set to true

Expected behavior If permission is set to "Ask every time" the app should prompt the window with question to set the permissions again.

Screenshots If applicable, add screenshots to help explain your problem.

Version v2.0.0

Smartphone (please complete the following information):

  • Device: [Samsung A70]
  • OS: [Android 11]

Additional context Add any other context about the problem here.

denisLoncaric avatar Aug 04 '21 12:08 denisLoncaric

The cause of this is the (unfortunately apparently very common) misuse of the shouldShowRequestPermissionRationale method. It is used to detect the difference between "denied for now" and "denied forever", which is not its function at all.

Capacitor itself also makes this wrong assumption about the return value of this function: https://github.com/ionic-team/capacitor/blob/17a73ff60b3e0e1807db3a2349b83ca62d7fb83b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java#L899-L905 I don't know if this issue is caused by that or not.

The guidelines regarding permissions in the readme also explicitly do it "the wrong way" compared to the official guidelines regarding "asking for permissions" on Android: https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions

In my opinion the functionality (on Android) should be as follows:

  • App invokes Plugin method checkPermission({ force: false }) to determine whether or not the permission is already granted. If yes, great. If not, keep going.
  • CheckPermissionResult gets a new property showRationale which is set to the result of shouldShowRequestPermissionRationale if force is false.
  • If the App receives showRationale: true from checkPermission({ force: false }), it shows a rationale. If the user does not acknowledge the need, bail out here. If they do, keep going.
  • App invokes Plugin method checkPermission({ force: true }). The Plugin unconditionally requests the permission if this happens.
  • App receives result of checkPermission({ force: true }). If granted is false, we do not have the permission and need to gracefully degrate. If granted is true, great.

In Pseudocode (App):

async function hasPermission(): boolean {
    const passiveResult = await BarcodeScanner.checkPermission({force: false});
    // we already have the permission, great
    if (passiveResult.granted) return true;
    // we will never have the permission, even if we ask using force
    if (passiveResult.denied || passiveResult.restricted) return false;

    // if we don't need to show a rationale _or_ the user acknowledges our rationale, request the permission
    if (!passiveResult.showRationale || await showPermissionRationale()) {
        return (await BarcodeScanner.checkPermission({force: true})).granted;
    } else {
        // user denied our rationale
        return false;
    }
}

On iOS the story is simpler in the Plugin (see: https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios section "Verify and Request Authorization for Capture", this is already correct in the current implementation):

  • First check authorizationStatus(for:). If it returns granted, denied or restricted set the appropriate granted/denied/restricted property.
  • If authorizationStatus(for:) returned notDetermined:
    • If force is false, set showRationale to false and wait for the app to call checkPermission again with force: true
    • If force is true, call requestAccess and wait for the completion handler.

An Addendum: Doing this properly also simplifies things, mainly the need to keep track of "are we asking for the first time" on Android using SharedPreferences completely goes away as well as the code to open the settings app.

diesieben07 avatar Aug 24 '21 13:08 diesieben07

I'm also running into this issue, it would be nice to get a solution for this

hrafnkellbaldurs avatar Nov 05 '21 11:11 hrafnkellbaldurs

Same issue here right now. It prevents the app from having a decent UI to handle this use case.

leye0 avatar Nov 06 '21 02:11 leye0

Is this project still maintained?

alendosev-jumbo avatar Nov 30 '21 14:11 alendosev-jumbo

I am using the checkPermissions / requestPermissions of the @capacitor/camera module to check for permissions and then I use the barcode module to do the scanning.

jorrit avatar Dec 30 '21 09:12 jorrit

@jorrit solution should do the trick for now, once I cleaned up the open issues and PRs I think it would make sense to use the same logic implemented in @capacitor/camera for this plugin.

thegnuu avatar Feb 07 '22 10:02 thegnuu

I will start a PoC to use ML Kit for this plugin soon (#107), when doing this I will as well optimise the permission checks and mimic the behaviour of @capacitor/camera since the rewrite will include a lot of breaking API changes anyways and therefore some work to update to 3.0.0 it will be possible to split the checking to checkPermissions and requestPermissions as well.

thegnuu avatar Mar 04 '22 10:03 thegnuu

I have rewritten the permissions according to capacitor/camera this should work in the upcoming release v5 as expected, this version is currently released as pre-release.

thegnuu avatar May 08 '23 14:05 thegnuu

Could you please also apply this change to v4? Since the ML Kit used in v5 has a much larger impact on package size, it's possible that some people who are concerned about package size would prefer to use v4. Thank you very much.

wenxuanjun avatar Jul 23 '23 16:07 wenxuanjun