shouldShowRequestPermissionRationale always returns false - android

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.

Related

Android: how to use shouldShowRequestPermissionRationale to check permission state?

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?

Is it possible to revoke permission with Espresso?

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);
}
}
}

The shouldShowRequestPermissionRationale method returns false the first time

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.

Requesting android permissions only once

When we ask permission from the user on his phone, is there a way we can inform the OS not to ask this permission again? No matter what the user's response is.
If the program does not "desperately" require the permission, you can use SharedPreferences and store a variable called firstTime withe boolean value of true.
Then check in the onCreate method for that variable. if it is true, ask the permission then set the value to false and save it. Then when the user will rerun the application, it won't be the "FirstTime" so he won't be prompted with the permission.
SharedPreferences pref = this.getSharedPreferences("PACKAGE.NAME",MODE_PRIVATE);
Boolean firstTime = pref.getBoolean("firstTime",true);
if(firstTime){
//Ask for permissions here
pref.edit().putBoolean("firstTime",false).apply();
}
No, sorry. You would need to keep track of whether you asked the user for permissions, then use that information to determine whether or not to ask in the future.
When you ask the permission in any way like in the following, in the dialog that would be open to ask for permission there is a check box that by checking it, OS would not ask you for that permission any more:
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_WRITE_EXTERNAL_STORAGE);
}
}

Cordova runtime permissions

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.

Categories

Resources