From the doc, shouldShowRequestPermissionRationale indicates whether you should show permission rationale UI.
I found:
if change one permission to "Notify" and before first showing the permission-request-dialog, shouldShowRequestPermissionRationale return false
if deny the permission with the "Never ask again" checkbox selected, shouldShowRequestPermissionRationale return false
Then I met a problem, how to distinguish these two cases? I mean, in the following code snippet from Android developer:
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected. In this UI,
// include a "cancel" or "no thanks" button that allows the user to
// continue using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
new String[] { Manifest.permission.REQUESTED_PERMISSION },
REQUEST_CODE);
}
In the case 2, though denied the permission with "Never ask again", the code still invokes requestPermissions?
Related
I want to request permissions on runtime.I checked out official android developer website and it says that shouldShowRequestPermissionRationale returns true if permission was previously denied and returns false if permission has been denied AND never ask again checkbox was selected.
Then i saw this code in the site:
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
My 2 questions are:
1)What happens if the user wasn't asked permission previously??We need to ask him right??Where do you put that code??
2)The above code asks for permission even when the user checked the never ask again checkbox(when shouldShowRequestPermissionRationale returns false,I.e,in the else block).How can u ask for permission when the user has checked that option??
To answer both your questions:
You can first use checkSelfPermission() to see if the permission has already been granted. If it has not been granted then you should check if shouldShowRequestPermissionRationale() returns true or false.
shouldShowRequestPermissionRationale() will return true in the following case:
When the user has denied the permission previously but has not
checked the "Never Ask Again" checkbox.
shouldShowRequestPermissionRationale() will return false in the following 2 cases:
When the user has denied the permission previously AND never ask
again checkbox was selected.
When the user is requesting permission for the first time.
So, what you can do is, if shouldShowRequestPermissionRationale() returns false
you can use a boolean preference value (default value as true) to check for
first time request of permission in the else case, if it is the first
request, then trigger requestPermissions
else if it is not the first request and the user has previously denied the request and also has checked the "Never ask again" checkbox, you can show a simple toast with the reason for unavailability of the feature that requires the permission and also mention the steps to manually enable it via settings.
Something like this:
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
if(isFirstTimeRequest){
// No explanation needed; request the permission
// RESET PREFERENCE FLAG
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
} else {
// User denied previously and has checked "Never ask again"
// show a toast with steps to manually enable it via settings
}
}
I'm currently writing some tests for an app. If the app does not have storage permission, a fragment pops up. I know how to grant permission with "grant permission rule" but is there a method to revoke permission?
No, that is not possible. Quote from https://developer.android.com/reference/android/support/test/rule/GrantPermissionRule:
Once a permission is granted it will apply for all tests running in the current Instrumentation. There is no way of revoking a permission after it was granted. Attempting to do so will crash the Instrumentation process.
What I can suggest is to wrap permission methods into some service and mock the value checking if the permission is granted
Yes, during testing its possible...Feuby gave a somewhat correct answer to the same question in this SO 2017 question
Android revoke permission at start of each test
Repeating his code
public static boolean checkCameraPermission(MainActivity thisActivity) {
return ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
public static void checkAndAskCameraPermission(final MainActivity thisActivity) {
if (!checkCameraPermission(thisActivity)) {
//No right is granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CAMERA)) {
//Open a dialog explaining why you are asking permission then when when positive button is triggered, call this line
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CAMERA},
CHECK_FOR_CAMERA_PERMISSION);
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CAMERA},
CHECK_FOR_CAMERA_PERMISSION);
}
}
}
Currently I am programming an android app which reads out the phone number of the user. Because the app is developed for SDK 23+ (target sdk 27) I have to request the read sms permission. If the user denies the permission the first time a dialog should appear where the use of the phone number is explained. Then the user can choose to request the permission once more or to type the phone number manually. If the user tiks "never ask again" a dialog should appear where the user is instructed to allow the permission via the settings.
To check if the user has ticked never ask again, I use the method shouldShowRequestPermissionRationale. But the method always returns false, even if I never ticked never ask again.
Here is the relevant code:
public class SettingsSettingsFragment extends Fragment implements ActivityCompat.OnRequestPermissionsResultCallback {
public void readNumber() {
if (person.getPhoneNumber() == null || person.getPhoneNumber().equalsIgnoreCase("")) {
if (checkSelfPermission(getActivity(), READ_SMS) != PackageManager.PERMISSION_GRANTED)
requestPermission();
else {
//read phoneNumber
}
}
//request permission send sms
private void requestPermission() {
requestPermissions(new String[]{Manifest.permission.READ_SMS}, PERMISSION_REQUEST_SEND_SMS);
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_SEND_SMS)
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
// user rejected the permission
boolean showRationale = shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS);
if (!showRationale) {
// user also CHECKED "never ask again" - show dialog
//show dialog: please allow in settings
} else if (counter < 2) {
// explain the permission, and give the user the possibility to ask once more
counter++;
}
}
Why is shouldShowRequestPermissionRationale always returning false?
The code is from here: Android M - check runtime permission - how to determine if the user checked "Never ask again"?
Thanks for help in advance.
According to android developers documentation of shouldShowRequestPermissionRationale():
method returns true if the app has requested this permission previously and the user denied the request.
If the user turned down the permission request in the past and chose the Don't ask again option in the permission request system dialog, this method returns false
If you haven't requested permission before it will return false because there is no need to pop the rational alert dialog.
This answer was very helpful for me to understand the issue.
Your requestPermission() is for READ_SMS, butshouldShowRequestPermissionRationale() is about SEND_SMS.
shouldShowRequestPermissionRationale() keeps returning false until user is asked for the "relevant" permission. Once requested, it returns true until user denies the same permission with "Never Ask Again" checked.
So, in your case, SEND_SMS is never requested. Therefore shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS) will keep returning false as expected. I made the same mistake before.
The shouldShowRequestPermissionRationale method returns false the first time.
I have the following code in a Fragment:
if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
requestPermissions(new String[]{READ_CONTACTS}, 0);
} else {
Toast.makeText(getActivity(), "FALSE", Toast.LENGTH_SHORT).show();
snackBarInfo.dismiss();
}
Has anyone else encountered this?
Yes, this is by design. The idea is that if the permission is not granted and the shouldShowRequestPermissionRationale method returns false, then the app should request the permission from the OS.
When the user denies the permission request, then the shouldShowRequestPermissionRationale method will return true. At this point, you should show some custom UI to the user explaining why the permission is required.
See the Workflow for requesting permissions section in the Request app permissions page for further details.
I'm trying to get the user to enable the location group permission for my app with Cordova's hasPermission/requestPermission methods, but the results are confusing...
When I call hasPermission with ACCESS_FINE_LOCATION, it always returns true. Calling this on Manifest.permission_group.LOCATION seems to return true/false appropriately.
Calling requestPermission with Manifest.permission_group.LOCATION doesn't present a system dialog, so I'm calling this with ACCESS_FINE_LOCATION to get the dialog.
The dialog Allow button turns on the Location group permission for my app and calls onRequestPermissionResult with PackageManager.PERMISSION_GRANTED, but the Deny button also returns ...GRANTED, leaving the Location group permission off.
For illustration, here's my current code:
private void checkPermissions() {
if (!cordova.hasPermission(Manifest.permission_group.LOCATION)) {
cordova.requestPermission(this, PERMISSION_RUNTIME_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION);
}
}
#Override
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
if (permissions.length != 1 || grantResults.length != 1 || !Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0])) {
throw new RuntimeException("Unexpected permission results " + Arrays.toString(permissions) + ", " + Arrays.toString(grantResults));
}
int result = grantResults[0];
String action = null;
switch (result) {
case PackageManager.PERMISSION_DENIED:
action = Constants.ACTION_RUNTIME_PERMISSION_DENIED;
break;
case PackageManager.PERMISSION_GRANTED:
action = Constants.ACTION_RUNTIME_PERMISSION_GRANTED;
break;
default:
throw new RuntimeException("Unexpected permission result int " + result);
}
Intent i = new Intent(action);
i.putExtra("permission", Constants.EXTRA_RUNTIME_PERMISSION_NOTIFICATION_ID);
getContext().sendBroadcast(i);
}
What's the right way to handle this? Sometimes using an individual permission and sometimes using a group with these methods doesn't seem right - I would expect this to be consistent. My guess is that the PERMISSION_GRANTED after the DENY button is pushed on the dialog is because I'm requesting an individual permission, which is on even though the group is off; is there a way to detect that the user denied the request?
I have a lot of questions there which basically boil down to "how do I either get the user to enable the Location group permission when it's off or know when they decline"?
If it helps, my android-targetSdkVersion is set to 22, and I'm using Cordova 6.1.1.
If it helps, my android-targetSdkVersion is set to 22, and I'm using Cordova 6.1.1.
Android run-time permissions were only introduced in API 23, so if your android-targetSdkVersion is set to 22, run-time permissions code will always return GRANTED for any permission, since permissions are granted at installation time via the manifest.
However, if your app is displaying runtime permissions dialogs, I'm guessing that you must be building against API 23 and using cordova-android#5+ for the Android platform.
Regarding permission groups vs individual permissions, you should read the Android documentation regarding runtime permissions:
The dialog box shown by the system describes the permission group your app needs access to; it does not list the specific permission. For example, if you request the READ_CONTACTS permission, the system dialog box just says your app needs access to the device's contacts. The user only needs to grant permission once for each permission group. If your app requests any other permissions in that group (that are listed in your app manifest), the system automatically grants them. When you request the permission, the system calls your onRequestPermissionsResult() callback method and passes PERMISSION_GRANTED, the same way it would if the user had explicitly granted your request through the system dialog box.
So in your case, requesting ACCESS_FINE_LOCATION grants access to all permissions in the LOCATION group (you can find the full list of groups and permissions here).
the Deny button also returns ...GRANTED, leaving the Location group permission off.
This should not return GRANTED. If the Deny button is pressed, access will be DENIED to that entire permission group (including the requested permission). The logic in your code snippet looks OK to handle this, so I would use the step-through debugger in Android Studio to see exactly what is happening in your code here.