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)
Related
I'm invoking the following:
ActivityCompat.requestPermissions(activity, permissions, requestCode);
I request the following permissions:
Manifest.permission.ACCESS_FINE_LOCATION
Manifest.permission.ACCESS_BACKGROUND_LOCATION
Manifest.permission.RECORD_AUDIO
Manifest.permission.MODIFY_AUDIO_SETTINGS
Now, my app requires that the user select "All the time" for the location permission. So if the user selects "Only while using this app", Android will show a subsequent screen asking them to select "All the time".
The problem is that the user sees the initial location permission request, followed by the audio request, followed by the "All the time" location request.
It seems no matter what order I make the list "permissions" parameter in the requestPermissions method, I can't make audio be requested first.
Any ideas of how to get this done? Or if Android by default requests them in a fixed order?
You need to request course or precise location FIRST, then when one or both of them is granted you can request background permission. You can't request them at the same time.
Or you can just use this library that I have been working on for a while. It is inspired by how google fit is doing it.
https://github.com/warting/permissions-ui
It is also dealing with different android SDK:s, rationales and permanent denied scenarios.
Only ask for background and audio permissions when needed. Not both at the same time just because you might need it later.
I'm updating an app that currently uses the ACCESS_FINE_LOCATION permission and with new API 30 requirements, will now require the ACCESS_BACKGROUND_LOCATION permission as well.
While updating the app to add this permission, I'm also trying to use the new ActivityResultLauncher callback method of requesting permissions (https://developer.android.com/training/permissions/requesting#allow-system-manage-request-code)
This seems to work well using launching single requests along these lines:
ActivityResultLauncher<String> launcher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(), this::processPermissionResult);
launcher.launch(somePermission);
However, since I need both ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION it requires some extra code to keep track of the results from multiple callbacks (since each request will generate its own callback) so that I can take appropriate action if one, both, or none of the permissions were granted.
To simplify this a bit, I was looking to use the RequestMultiplePermissions method instead, which would conveniently give me one callback with all the results in a map:
ActivityResultLauncher<String[]> launcher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(), this::processPermissionResult);
launcher.launch(new String[] {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
});
However I found that the moment I request both ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION in the same request, I am no longer prompted with either permissions dialog and the callback comes back with false for both the permissions.
I can, however, make individual requests for these permissions using RequestMultiplePermissions by giving it an array containing a single permission, and then calling launch multiple times using the same approach as if I was using the single RequestPermission call, and that will work.
It seems that there some conflict/interaction between the ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION permissions when it comes to RequestMultiplePermissions? I was able to take out ACCESS_BACKGROUND_LOCATION and use a different permission, like WRITE_EXTERNAL_STORAGE, and I would get two separate permission dialog prompts.
Am I misunderstanding something about the ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION permissions? Should I not be requesting them simultaneously?
I have following permissions:
private static final String[] LOCATION_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
};
I try to request permissions in my code with following line (activity is my current activity ref):
ActivityCompat.requestPermissions(activity, LOCATION_PERMISSIONS, 1);
Everything works fine if I have targetSdkVersion 29 or lower in my build.gradle file. Permissions dialog appear, I can press button in it to grant permissions. Everythig is fine
But! After I changed target sdk to Android 11: targetSdkVersion 30 this functionality stop working.
Permission system dialog doesn't appear - and I can't grant location permissions for my app.
So can anybody help me? What am I wrong?
What should I change in my code - for correctly working with Android 11?
Now with targetSdkVersion 29 I can run my app under the android 11 emulator too. May be I shouldn't increment target sdk version to: 30 at all?
The solution is:
STEP 1: The app should request foreground location permissions :
private static final String[] FOREGROUND_LOCATION_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION, // GPS
Manifest.permission.ACCESS_COARSE_LOCATION, // GPS approximate location
};
STEP 2: The app should request background location permissions :
#RequiresApi(api = Build.VERSION_CODES.Q)
private static final String[] BACKGROUND_LOCATION_PERMISSIONS = {
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
};
Only in this order!
ActivityCompat.requestPermissions(activity, FOREGROUND_LOCATION_PERMISSIONS, 1);
// Check the first statement grant needed permissions, and then run the second line:
ActivityCompat.requestPermissions(activity, BACKGROUND_LOCATION_PERMISSIONS, 1);
If I firstly trying to request background permission - it will not work.
If I request only background permissions (without already granted foreground permissions) - it will not work.
I should request foreground permissions - and then after that I should request background permission - one by one.
On android 11 requests for background location permission will be implicitly denied if user haven't granted foreground location permission first.
Requests containing both foreground and background location permission at the same time are deemed invalid:
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.
See Request location access at runtime
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);
I am working on BLE Bluetooth scanning is working on all devices except the Android 10. Android 10 is not working, anyone please answer the question for the version 10 issues for scanning BLE Bluetooth
To make BLE scanning work on Android apps targeting Android 10 you need to ask the user for
ACCESS_BACKGROUND_LOCATION
along with ACCESS_FINE_LOCATION and also don't forget to add the permission in the manifest:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Here's the explanation why:
Android 10 (API level 29) introduces a number of features and behavior changes to better protect users' privacy. These changes extend the transparency and control that users have over their data and the capabilities they give to apps.
Your problem in short:
Access to device location in the background requires permission
To support the additional control that users have over an app's access to location information, Android 10 introduces the ACCESS_BACKGROUND_LOCATION permission.
Unlike the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions, the ACCESS_BACKGROUND_LOCATION permission only affects an app's access to location when it runs in the background. An app is considered to be accessing location in the background unless one of the following conditions is satisfied:
An activity belonging to the app is visible.
The app is running a foreground service that has declared a foreground service type of location.
To declare the foreground service type for a service in your app, set your app's targetSdkVersion or compileSdkVersion to 29 or higher. Learn more about how foreground services can continue user-initiated actions that require access to location.
Hope this fixes your problem :)
You need to have location enabled to perform a BLE scan on android 10.
To check if location is enabled and ask user to enable location service (Kotlin):
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!LocationManagerCompat.isLocationEnabled(lm)) {
// Start Location Settings Activity, you should explain to the user why he need to enable location before.
startActivity(Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS))
}
LocationManagerCompat class is available in androidx.appcompat:appcompat:1.1.0 dependency, if you don't use androidx, you can use instead :
lm.isLocationEnabled()
Adding the following permissions to AndroidManifest.xml is not enough
android.permission.ACCESS_FINE_LOCATION,
android.permission.ACCESS_COARSE_LOCATION,
android.permission.ACCESS_BACKGROUND_LOCATION.
Add permissions to MainActivity such as
ActivityCompat.requestPermissions(
this,
new String[]
{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE
}, 0);
I faced the same problem, and I have just fixed it using cordova-plugin-android-permissions.
Android 10 (SDK 29) requires ACCESS_FINE_LOCATION, while previous versions require only ACCESS_COARSE_LOCATION. (more info in this Android Developers page)
Unlike some other answers state, the ACCESS_BACKGROUD_LOCATION is not required. It's better not to require it unless you really need it. Play Store considers background location a dangerous permission, and will ask you to fill in some forms to justify why do you need it.
Old plugins would ask only ACCESS_COARSE_LOCATION permission, which was enough for Android 9 (SDK 28) and older versions. The best solution is to update the plugin. But if it is hard to do, there is an easy workaround.
Add cordova-plugin-android-permissions (npm link) to your project, and use it to ask ACCESS_FINE_LOCATION permission.
Add this code to your "device ready" function to check if your app already has this permission or not:
permissions = cordova.plugins.permissions;
permissions.checkPermission(permissions.ACCESS_FINE_LOCATION, checkPermissionSuccessCallback, checkPermissionErrorCallback);
And then create the following callback function to ask user permission:
function checkPermissionSuccessCallback(status) {
if (status.hasPermission) {
console.log("Permission OK");
} else {
console.log("Permission not OK");
// ask user permission
permissions.requestPermission(permissions.ACCESS_FINE_LOCATION, requestPermissionSuccessCallback, requestPermissionErrorCallback);
}
}
You don't need to add anything to your "config.xml" to get this permission.
try this
//gradle
dependencies {
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.3'
}
//import
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
//init scan setting
final ScanSettings settings = new ScanSettings.Builder().setReportDelay(1000).setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
//start scan
scanner.startScan(null, settings, scanCallback);
//scan callback
private final ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(final int callbackType, #NonNull final ScanResult result) {
super.onScanResult(callbackType, result);
}
public void onBatchScanResults(#NonNull final List<ScanResult> results) {
for (final ScanResult result : results) {
//result.getDevice() is scanned device
}
}
#Override
public void onScanFailed(final int errorCode) {
// should never be called
}
};
For me it is working if i use fine permission in AndroidManifest. Switch on the location in Settings of the phone manually, and set app permission to location.
I suppose in android 10 having just the location permission is not enough. Your location service should be turned on while you are scanning for your device. So, ask the user for turning on the location service if it is turned off.
Alternative Solution:
You can use Companion device pairing for android 8 and higher. Mind you, In my opinion, it is not well documented, and it might be a bit unstable. For example, when I assigned a scan filter (BLE Service UUID) the Companion device pairing crashed, but without that filter, it worked.
Try setting the callbackType to CALLBACK_TYPE_ALL_MATCHES, it solves the problem for me.
val scanSettings = ScanSettings.Builder()
scanSettings.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
scanSettings.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
scanSettings.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
val filter = ScanFilter.Builder()
filter.setServiceUuid(ParcelUuid(Service_UUID))
bluetooth.bluetoothLeScanner.startScan(mutableListOf<ScanFilter>(filter.build()), scanSettings.build(), callback)
User need to allow "Location" - (GPS) on Device