To improve @AndreasGobs answer, below the code to test if the connection with a device is viable or not based on current available permissions.
In the manifest I've set that the COARSE and FINE location permissions must be limited to max API 30.
Tested on Android 6, 8.1, 11 and 12 devices.
I hope this will be useful.
/**
* - API < S
* - Check ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions
* - API < O
* - Check has GPS
* - Check GPS enabled
* - API >= S
* - Check BLUETOOTH_SCAN permission
* - Check BLUETOOTH_CONNECT permission
* - Check Bluetooth enabled
*/
private boolean canConnect(){
Timber.d("canConnect called");
List<String> deniedPermissions = new ArrayList<>();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (!checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION))
deniedPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
if (!checkPermission(Manifest.permission.ACCESS_FINE_LOCATION))
deniedPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
if(deniedPermissions.isEmpty()){
if (!MmcDeviceCapabilities.hasLocationGps() //check if the device has GPS
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|| MmcDeviceCapabilities.isGpsEnabled()){ //check if the GPS is enabled
if(MmcDeviceCapabilities.bluetoothEnabled()) //check if bluetooth is enabled
return true;
else {
requestEnableBluetooth(); //method to request enable bluetooth
return false;
}
}
else {
Timber.d("Request enable GPS");
requestEnableGps(); //method to request enable GPS (improving devices scan)
return false;
}
}
else {
Timber.d("Request GPS permissions");
requestRuntimePermissions(
"Bluetooth GPS request",
"GPS permissions request rationale",
GPS_PERMISSIONS_CODE,
deniedPermissions.toArray(new String[0]));
return false;
}
}
else { // Build.VERSION_CODES.S or later
if(!checkPermission(Manifest.permission.BLUETOOTH_SCAN))
deniedPermissions.add(Manifest.permission.BLUETOOTH_SCAN);
if(!checkPermission(Manifest.permission.BLUETOOTH_CONNECT))
deniedPermissions.add(Manifest.permission.BLUETOOTH_CONNECT);
if(deniedPermissions.isEmpty())
if(MmcDeviceCapabilities.bluetoothEnabled()) //check if bluetooth is enabled
return true;
else {
requestEnableBluetooth(); //method to request enable bluetooth
return false;
}
else {
Timber.d("Request bluetooth permissions");
requestRuntimePermissions(
"Bluetooth permissions request",
"Bluetooth permissions request rationale",
CONNECT_PERMISSIONS_CODE,
deniedPermissions.toArray(new String[0]));
return false;
}
}
}
/**
* This method checks if a runtime permission has been granted.
* @param permission The permission to check.
* @return <code>TRUE</code> if the permission has been granted, <code>FALSE</code> otherwise.
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean checkPermission(@NonNull String permission){
return ActivityCompat.checkSelfPermission(this, permission)
== PackageManager.PERMISSION_GRANTED;
}
private void requestRuntimePermissions(@NonNull String title, @NonNull String description, int requestCode, @NonNull String... permissions){
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder
.setTitle(title)
.setMessage(description)
.setCancelable(false)
.setNegativeButton(android.R.string.no, (dialog, id) -> {
//do nothing
})
.setPositiveButton(android.R.string.ok, (dialog, id) -> ActivityCompat.requestPermissions(this, permissions, requestCode));
showDialog(builder); //method to show a dialog
}
else ActivityCompat.requestPermissions(this, permissions, requestCode);
}
BLUETOOTH_SCAN
permission needed to enable the adapter or justBLUETOOTH_CONNECT
? – Rhodian