I am developing an app that allows a user to connect to a specific wifi network using the google vision api to scan a QR code containing an ssid. To scan for wifi networks I need to request the user to grant the location permission. This all works fine if i go to the settings and manually enable the Location permission but when requesting the permission at run-time, the user taps enable then a second dialog pops up, Screen Overlay Detected which requires a context switch to enable the Screen Overlay permission. I am not drawing over other apps and when i manually enable the location permission there is no problems. So why is this permission dialog popping up?
As I am not sure why Android is asking for this permission i am not sure what code to post. i have included the request for the 3 required permission for the change network request.
final int permissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA);
final int locationPermissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION);
final int wifiPermissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CHANGE_WIFI_STATE);
final int granted = PackageManager.PERMISSION_GRANTED;
if ((permissionCheck != granted) || (locationPermissionCheck != granted) || (wifiPermissionCheck != granted)) {
ActivityCompat.requestPermissions(mActivity, new String[]{
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.CHANGE_WIFI_STATE
}, 0);
} else {
cameraSource.start(cameraView.getHolder());
}
Update
Turns out its not just the location permission that trips it up. Any permission request is experiencing the same behaviour.
It may be some libraries you are using in your that requires screen overlay permission. Can you check the libraries you are using in your and the permission they require.
Related
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.
I have an app to get user location with permissions.My target API=25 btw
Once I have given permission that It doesn't ask me for request permission anymore.
I have checked with debug my hasCoarseLocationPermission and hasFineLocationPermission always return 0(GRANTED) after giving permission.
But if I clear the app data asking for permission again(returns -1).But I always want to ask for permission when I open the application(must return -1 not 0).How can i do that?
Thanks in advance.
int hasCoarseLocationPermission=ContextCompat.checkSelfPermission(Kirala3.this, Manifest.permission.ACCESS_COARSE_LOCATION);
int hasFineLocationPermission = ContextCompat.checkSelfPermission(Kirala3.this, Manifest.permission.ACCESS_FINE_LOCATION);
I've tried also with PermissionChecker function but it returns same value(0).
Try with ActivityCompat
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
Use this function for checking location permission
public boolean isLocationPermissionEnabled() {
return !(Build.VERSION.SDK_INT >= 23 &&
ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED);
}
Asking the user for permissions is not something an app is supposed to do at each launch. Once the app has been granted a permission, it has the permission until it is revoked by the user. What you describe is happening sounds correct:
First launch the app calls checkSelfPermission and gets back PERMISSION_DENIED
The app requests the permission and is granted by the user
Subsequent launches of the app also still call checkSelfPermission (which is exactly what it should do.) Now the returned value is PERMISSION_GRANTED. This is expected. No new permission request from the user is required.
Manually reset the app data and revoke the permissions in the Settings app
Launch the app again and checkSelfPermission returns PERMISSION_DENIED, the app must ask the user for permission again.
You may find this talk helpful: https://youtu.be/WGz-alwVh8A
You may also find this permissions helper library useful: https://github.com/hiqes/andele
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.
I have request the permission in android version 6.0 - Marshmallow,But it still return empty list when using getScanResults().
private boolean checkPermission() {
List<String> permissionsList = new ArrayList<String>();
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (permissionsList.size() > 0) {
ActivityCompat.requestPermissions((Activity) mContext, permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
After request permission, then in the onRequestPermissionsResult method,I have get the permission of ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION, But I still can not the scan result
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
if (permissions.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED ||
(permissions.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED)){
List<ScanResult> scanResults = mWifi.getScanResults();
//list is still empty
}
else {
// Permission Denied
Toast.makeText(mContext, getString(R.string.permission_deny), Toast.LENGTH_LONG).show();
}
break;
}
}
Is this a bug of android M?
You still need to enable WIFI after you request the permission. So in short, you have to do this in sequence for scanning the perimeter:
Request the necessary permissions (ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION). Additionally, on MM you need to request this at run-time, as you stated.
Enable WIFI with WifiManager#setWifiEnabled(true);
You don't have to enable location access programatically that I know off. But read the note below.
You have to register a BrodcastReceiver for SCAN_RESULTS_AVAILABLE_ACTION. This is where you get the signal that the scans are ready. Doesn't matter if you register through AndroidManifest or dynamically at run-time, as long as it's done before the next step.
You have to WifiManager#startScan() in order to request exactly ONE update for network scans. If you want more, set up a timer/timertask (recommended) or reschedule when you receive the previous one (which might never come)
Only on the BroadcastReceiver onReceive will you be able to call WifiManager#getScanResults() with plausible results.
Note: On some phones (Moto X 2014), I noticed you need basic location enabled to get any results, which only the user (system UI) seems to be able to do trigger on/off. If the user has location completely off, I can't seem to get a non-empty result list, even though the system UI can. This is likely due to Marshmallow needs to have location for Bluetooth and WiFi scans in user apps, and a bad implementation by Motorola, or a defect already fixed in latest Marshmallow bug tracker but not in Motorola's latest OTA, because this doesn't happen in a Nexus 5 or a Galaxy S6.
on nexus 5, with M update, it appears to me I also need to have GPS location turned on to get this working.
Maybe it cause by android M's runtime permission management,you can set targetSdkVersion 19(less than 21 or 23) to try again. It works for me
In Android Marshmallow 6.0.1 go to Settings > Connections and hold down Location. There click on Location method and you will see three options:
High accuracy: uses GPS, Wi-Fi and mobile networks.
Battery saving: uses Wi-Fi and mobile networks.
Device only: uses GPS.
Select Battery saving in order to get Wi-Fi scan results without enabling GPS.
The permission is requested when needed at run-time. Note that it is NOT:
Settings -> Location
It is checking:
Settings -> Applications -> (YOUR APP) -> Permissions
That is where the more granular run-time permission must be set. It must be set for each application as of version 6.
I am updating my app to work with the new Android Marshmallow permission framework and it looks like it's enough for the user to grant the ACCESS_FINE_LOCATION permission at runtime for the app to work fine. This is what I do:
public static final String[] runtimePermissions = { permission.ACCESS_FINE_LOCATION };
public static final int LOCATION_PERMISSION_IDENTIFIER = 1;
and further down the class:
public static boolean checkConnectionPermission(final Context context) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (context.checkSelfPermission(permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
return true;
}
else {
((Activity) context).requestPermissions(runtimePermissions,
LOCATION_PERMISSION_IDENTIFIER);
return false;
}
}
// since in API levels below M the permission is granted on
// installation,
// it is considered a given that the permission has been granted since
// the
// app is running. So we return true by default
else {
return true;
}
}
I am just concerned that I am overlooking something that could cause trouble in the future (the app is near-production) with Security Exception(s).
I guess my ultimate question is: does granting FINE_LOCATION somehow auto-grant COARSE_LOCATION too?
Sort of already answered here.
If I have ACCESS_FINE_LOCATION already, can I omit ACCESS_COARSE_LOCATION?
Some additional information, you should look at permission group.
https://developer.android.com/preview/features/runtime-permissions.html
They are in the same permission group. If you really want to play safe, just include both in your manifest, and request them on need. It would be transparent to user.
According to this blog post, if you have specified both in the manifest and the user has granted you one, then when you ask for the other it will be automatically granted (since they're both in the same permission group).
That means that if you have been granted COARSE_LOCATION and then ask for FINE_LOCATION you can get it without the user being prompted, but the catch is that you still have to specify both in the manifest.
Apps can actually register for two types of location updates. Fine grained, which uses the Global Positioning System to get the device’s location accurate to within a few meters, vs coarse-grained, which uses the Wifi, Cell towers and other data available to the device to get a rough (accurate to 10s of meters) location of the device.
Choice is yours.