Open Bug 1814472 Opened 2 years ago Updated 1 year ago

[UX] permission handeling on android 11+

Categories

(Fenix :: General, enhancement)

All
Android
enhancement

Tracking

(Not tracked)

People

(Reporter: boek, Unassigned)

References

(Blocks 1 open bug)

Details

From github: https://github.com/mozilla-mobile/fenix/issues/28110.

We could consider rethinking permission handling for devices running android11+.

We need to handle "allow once" permission option introduced introduced in android11. Currently there is a bug that causes the app to treat "allow once" option as "denied" on the next run. Here is the video to demonstrate the problem:

https://user-images.githubusercontent.com/92760693/206191985-afa81cc1-771a-4346-b0b7-7fa23e22296c.mp4

Adding a rationale would helps us to comply with the latest guidelines by google on permission handling:
<img width="600" alt="image" src="https://user-images.githubusercontent.com/92760693/206182464-7a594e7f-cc41-41b7-a63f-00a20cd54309.png">

However, the guidelines does not mention the denied forever state which we also should take into account. It happens after the user has denied the app permission twice in a row and the system will block permission options dialog from showing forever (at that point we should help the user to navigate to app settings to change the allow the permission manually).

API for permissions in not a straightforward one, and the majority of the apps (even the most popular ones) handle this flow differently (and most of the time, not by the guidelines and failing to handle all of use-cases consistently, even Chrome). The challenging parts of the flow are: when to show the rationale, how to react to a permission denial and to a blocked permission window (two "denied" permissions). There is also an option to tap outside of the window, which makes telling them apart not possible without very hacky solutions.

┆Issue is synchronized with this Jira Task

Change performed by the Move to Bugzilla add-on.

Type: defect → enhancement
Flags: needinfo?(mavduevskiy)

Oops, sorry, didn't mean to NI 😮‍💨

Flags: needinfo?(mavduevskiy)
Severity: -- → N/A
Blocks: 1818423

Hey,

I have a PR with the associated issue 1815638 which is blocked by this.
Wanted to add some information from the past issue where the subject was discussed.

First from a technical point of view I'm aware of two ways to have information on the permission state :

  • shouldShowRequestPermissionRationale which from the documentation : Gets whether you should show UI with rationale before requesting a permission.
    This will be true only after the user deny one time the permission. If the user deny a second time it will switch back to false.
  • isPermissionGranted will be returned after we ask for a permission but we do not know if the user was truly prompted.
    It will return true when either the user accepted (now or in the past) or selected the Only once (which will apply for the rest of the runtime of the application).

When prompted the user will always see three possible actions : Accept, Only Once and Denied.
If the user deny twice in a row the permission then we will enter the Denied Forever state.
From my tests the user cannot set the forever denied state; if the user change the permission to denied in the setting then it will count as a first refusal (shouldShowRequestPermissionRationale will then return true).

I believe the possible states are :

  • Never prompted or Always ask

    • shouldShowRequestPermissionRationale is false
    • The permission prompt will be visible
  • Granted / Allowed once (will last for the runtime of the application)

    • shouldShowRequestPermissionRationale is false
    • The permission prompt will not be visible and isPermissionGranted=true
  • Denied one time

    • shouldShowRequestPermissionRationale is true
    • The permission prompt will be visible
  • Denied a second time in a row

    • shouldShowRequestPermissionRationale is false
    • The permission prompt will not be visible and isPermissionGranted=false

We can identify the transition when the app enter the state denied forever: shouldShowRequestPermissionRationale will switch from being true before asking to false after a second refusal.

In the context of the camera permission which is only used by the application to scan QR code ? I believe in general a Rationale will never be useful since the link between scanning and using Camera is quite understandable ?
I would expect that even in what would seem the less logical flow: a user click to scan then deny the permission, no explication is needed since what I would expect is that the user either changed opinion or misclicked. In both of those case showing a rationale would be an hindrance I believe.

With this in mind I proposed the following flow :

  • Always try to prompt permission (the user might see nothing)
  • Use a setting to register when we enter the Denied Forever state (I see no other way :()
  • Display the Permission Needed only when we believe the user did not see any prompt: isPermissionGranted=false and we believe we already were in Denied Forever state.

The important point is to always ask and rely on the internal state only to display the Permission Needed when we believe the user did not see any prompt. This allow to minimize the impact of an invalid state.

The possible desync would be :

  • We do not display the Permission Needed even when the user did not see the permission prompt.
    I believe this is impossible since the user cannot enter the Denied forever state outside of the application (setting permission as denied count as one refusal).
  • The user will be prompted with Permission Needed when he just refused, for this I believe the user would need to :
    • Refuse twice to enter the Denied Forever state.
    • Outside of the application switch the permission to allow to reset the denied state
    • Outside of the application switch the permission to deny (it will count as a refusal)
    • Deny the permission when prompted

Additionally made some tests using a phone running Android 10, from what I tested the handling is quite similar (outside of no Allowed Once) it's just that instead of the denied forever being automatic the user is presented with an additional deny forever option after declining a permission once.

Similarly a scenario exist where the user can be prompted by the Permission Needed even when he just refused, but it relies too in modifying the permission outside of the app in a similar way :

  • After refusing once the permission the user activate the Denied forever option
  • Outside of the application switch the permission to allow to reset the denied state
  • Outside of the application switch the permission to deny (it will count as a refusal)
  • When prompt again click Deny or Deny Forever

As mentioned this solution can be tested in #822.

You need to log in before you can comment on or make changes to this bug.