Android 11 users can’t grant background location permission? - android

As of Android 11, apps targeting SDK 30+ will not show a user an option to grant background location permission to an app more than once. If not initially granted, it requires users to go to a settings page. How do we bring a user to the proper settings page?
When a feature in your app requests background location on a device that runs Android 11 or higher, the system dialog doesn't include a button to enable background location access. In order to enable background location access, users must set the Allow all the time option for your app's location permission on a settings page, as described in the guide on how to Request background location.
https://developer.android.com/about/versions/11/privacy/location#change-details
The user-visible label of the settings option that grants background location (for example, Allow all the time in figure 3). You can callgetBackgroundPermissionOptionLabel() to get this label. The return value of this method is localized to the user's device language preference.
https://developer.android.com/training/location/permissions#request-location-access-runtime
While Android provides a new API to get this settings page label, there is no documented API to bring up this settings page directly. The closest you can come is to bring up the app-specific settings page as described below. From there, the user must perform at least two taps to drill down to Permissions -> Location to enable background access. This is an onerous process that many users will fail to complete.
The lack of an API to bring up a settings page has been documented for a long time in this question, but is far more important as of the release of Android 11, as there is no other way of granting background permission.
How to programmatically open the Permission Screen for a specific app on Android Marshmallow?
It is possible to bring the user to the proper settings page the very first time the user is asked using code like this: requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), PERMISSION_REQUEST_BACKGROUND_LOCATION). This will work only once. If the user denies the permission (or even accidentally hits back or leaves the screen without granting), this will never work again, and the user must manually drill-down in settings as described above.
Is there there really no way for an app to help users grant background location permission after an initial denial other than to instruct them to go hunting for the right page in Settings?
Am I missing something? If not, isn’t this a major Android 11 usability problem?
A full example of code needed to trigger the proper settings page in a first time prompt, but the inability to do it ever again is here:
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
val builder =
AlertDialog.Builder(this)
builder.setTitle("This app needs background location access")
builder.setMessage("Please grant location access so this app can detect beacons in the background.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
PERMISSION_REQUEST_BACKGROUND_LOCATION
)
}
builder.show()
} else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
val builder =
AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons in the background. Please go to Settings -> Applications -> Permissions and grant background location access to this app.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri: Uri = Uri.fromParts("package", packageName, null)
intent.data = uri
// This will take the user to a page where they have to click twice to drill down to grant the permission
startActivity(intent)
}
builder.show()
}
}
}
} else {
if (!shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION
/*Manifest.permission.ACCESS_BACKGROUND_LOCATION*/
),
PERMISSION_REQUEST_FINE_LOCATION
)
} else {
val builder = AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons. Please go to Settings -> Applications -> Permissions and grant location access to this app.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri: Uri = Uri.fromParts("package", packageName, null)
intent.data = uri
// This will take the user to a page where they have to click twice to drill down to grant the permission
startActivity(intent)
}
builder.show()
}
}

Credits for the answer to #Stephen Ruda
I have run into the exact same problem. I agree that this is an issue for any developer who needs background location permission. I would like to add additional notes for other readers:
(1) On API 30+ you will first need basic location permissions before asking for background location permission - otherwise, it won't go to the permission screen at all.
(2) When you ask for background location permission and it sends them to the permission request screen, it will only 'lock' the user out if they ONLY hit the back button. If they tap any of the options and then back the request will work again.

It shouldn't be any different but this works for me every time (not only once):
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, REQUEST_BACKGROUND_LOCATION_PERMISSION);

Related

Android Development: Able to Request a SinglePermission, but RequestMultiplePermissions shows no UI

Right now I am trying to ask the user for permission to track his/her fine location, following the docs. I was playing around with the code, and it seems that I'm able to request a single permission from the user no problem, but when trying to request multiple permissions (Fine, Course, Background location) the permission UI doesn't even show. What the heck gives? The code is nearly the same.
The code for multiple permissions is here
private fun requestPermissions() {
val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { granted ->
Timber.tag("ASDASD").e(granted.toString())
// We can check if either the FINE or APPROX permission location has been
// granted.
}
if (TrackingUtility.hasLocationPermissions(requireContext())) {
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
requestPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
} else {
requestPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
)
}
}
https://imgur.com/a/AIDCy4Q - Single permission request works
https://imgur.com/a/T8sTznX - Multiple permission doesn't show anything
I tried following the documentation to a T, but it seems that nothing wants to work unless I use a SingleRequestPermission
According to the documentation:
Even if several features in your app require location access, it's likely that only some of them require background location access. Therefore, it's recommended that your app performs incremental requests for location permissions, asking for foreground location access and then background location access. By performing incremental requests, you give users more control and transparency because they can better understand which features in your app need background location access.
And then then afterwards it highlights:
Caution: If your app targets Android 11 (API level 30) or higher, the system enforces this best practice. If you request a foreground location permission and the background location permission at the same time, the system ignores the request and doesn't grant your app either permission.
So for For Android 11 and up, if you request background location together with fine or coarse location the system will ignore the request. You need to request for background location separately (After being granted the fine or coarse location access)

How to check if user chose "ask every time" for android permission

I've been working lately on a method that checks and asks for location permission. I followed android's guideline for implementing user permissions but I also added some stuff to manage the case when the user has denied the permission forever.
so what I did is use the shouldShowRequestPermissionRationale() which return false if the user hasn't been asked for the permission or the user chose "don't show again" when asked for it, and return true if the user denied the permission on previous occasion, I'm also storing a boolean in the shared preferences to check if this is the first time the user has been asked for this permission. which make this method a reliable way to determine if permission were denied forever or not (on API <30). you can check the code below.
when {
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED -> {
//location permission granted, get location info
}
shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) -> {
//user denied permission before but didn't chose "don't show again" option
//show rationale dialog
}
!shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& SharedPreferencesHelper.getInstance(
requireContext()
).locationPermissionAskedBefore() -> {
//user denied permission permanently, show dialog to head to app settings
}
else -> {
//haven't requested the permission before, request permission
}
}
the problem is when user change the permission state to ask every time from the settings on android 11 or above, the shouldShowRequestPermissionRationale() will now return false, and since its not the first time asking for the permission, the boolean saved in the shared preferences will return true and so the "permission denied permanently" code will run and the user would be asked to head to the settings and to change the permission instead of actually asking for the permission from the app itself.
So, what can I do to distinguish between permission being denied permanently and using ask every time?

Why (Manifest.permission.READ_EXTERNAL_STORAGE) return false when user granted all file access permission (ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS)?

I want to get storage permissions on Android 11 and later
After the user deny permission twice; I enable direct intent to the full storage licensing page with this flag:
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
The user grants this permission and even the condition:
ٍ Environment.isExternalStorageManager ()
Is also true, but still returns the Manifest.permission.READ_EXTERNAL_STORAGE permission false.
I do not understand why.
Can anyone guide me?
More description:
When user click to open gallery icon, i request get storage permission runtime and os appear a dialog to get user that choose between grant or deny that permission. in android 10 and lower, if user deny permission, every time user click on open gallery icon again, that dialog too appears again. but in android 11 and on wards, if user deny any runtime permission twice, os do not let to appear permission dialog anymore. in this case i start an intent with (Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) action flag to get manually this permission

REQUEST_IGNORE_BATTERY_OPTIMIZATIONS how to do it right

I have IntentService task in foreground mode, but in Android M+ the task stops in Doze mode. I read Google banned if the app uses intent to set themselves in whitelist. But if I use permission and check GRANT or DENIED, I get the granted result, but nothing happen. I don't see my app in whitelist. How can I add the app in whitelist without banned? (I added permission in AndroidManifest.xml)
if(Build.VERSION.SDK_INT>=23){
int permissionCheck= ContextCompat
.checkSelfPermission(this, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
if(permissionCheck == PackageManager.PERMISSION_DENIED){
//Should we show an explanation
if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)){
//Show an explanation
final String message = "";
Snackbar.make(coordinatorLayoutView,message,Snackbar.LENGTH_LONG)
.setAction("GRANT", new View.OnClickListener() {
#Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS }, PERMISSION_REQUEST_CODE);
}
})
.show();
}else{
//No explanation need,we can request the permission
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS }, PERMISSION_REQUEST_CODE);
}
}
}
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS is not a dangerous permission. You do not need, or want, any of that code. Quoting the documentation for REQUEST_IGNORE_BATTERY_OPTIMIZATIONS:
Permission an application must hold in order to use ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS. This is a normal permission: an app requesting it will always be granted the permission, without the user needing to approve or see it.
So, delete all that code.
I don't see my app in whitelist.
That is because the user did not add you to the whitelist, apparently.
Requesting REQUEST_IGNORE_BATTERY_OPTIMIZATIONS grants you the authority, from a security standpoint, to start an activity with an ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Intent. Be sure to include your app's package as the Uri:
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:"+getPackageName())));
The user will be taken to a screen where they can indicate that they are willing to suspend portions of Doze mode effects on your app.
How can I add the app in whitelist without banned?
If you do not want to be banned, do not do any of this. Have something in your app that starts an activity with an ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS Intent. This leads the user to the overall list of apps, where the user can toggle which ones are and are not on the whitelist. This does not require any permission.
The act of requesting REQUEST_IGNORE_BATTERY_OPTIMIZATIONS in the manifest is what may get you banned.
Be aware of using Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent for the activity, this does not work on all phones and you will get a android.content.ActivityNotFoundException. In particular it does not work on Samsung phones running Android 6. The only combination that I have found works on these phones is to declare the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS in the manifest, then launch an activity with intent Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS. I.e. the combination that is not liked by Google.

Go to My app's App Permission screen

Is there an Intent to go to my Application's "App Permissions" screen in Android-M?
I am making my App ready for Android-M and with the new permissions model. I have followed all the steps mentioned in the link
https://developer.android.com/training/permissions/requesting.html
Everything is set and all is good accept that if the user has checked the "Never ask again" button and denied permission, on next launch I want to give the user an option to go to the Application's "App Permissions" and change the permission himself, if he ever changes his mind. I wanted to make it a bit easier for the non-tech savvy user by providing a button which would take the user straight to my application's "App Permissions" screen. Is there a way? (It would be much better than giving the user instructions like Menu → Settings → Applications → Manage Applications → select application)
Thank you for helping out!
No, there is no intent to go directly to the Permissions screen.
However, just as in previous versions of Android, you can point people to your application's detail setting page using code such as:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
This will allow them to only hit a single button (the Permissions button on that screen) before they can access permissions.
Note that as per the UX around asking for permissions, consider linking to the settings page only as a last resort and only in cases where the permission is necessary for your app to function at all - ideally, you should show a strong rationale when shouldShowRequestPermissionRationale() returns true (i.e., they've denied it once but have not hit 'never ask again') such that the second time the user sees a permission dialog they know exactly why you need that permission. This means that users hitting 'never ask again' should be considered a very strong signal that the user will not ever grant you that permission.
Unfortunately this is not possible, however, as every other app does we can open the app's settings page so the user can grant the necessary permissions manually from there.
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)

Categories

Resources