Android разрешения
При в первом запуске приложения оно запрашивает разрешения, после их получения не работает поиск устройств по Bluetooth (BLE), будто бы приложению было отказано в разрешении. Но при перезапуске приложения все нормально работает, проблема именно после получения разрешений.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
leScanCallback = (device, rssi, scanRecord) -> {
if(device != null) {
this.runOnUiThread(() -> { updateScan(device, rssi); });
}
};
pbSearch = findViewById(R.id.pb_search);
btnSearch = findViewById(R.id.btn_search);
lvDevices = findViewById(R.id.lv_devices);
tvNoDeviceFound = findViewById(R.id.tv_no_device_found);
foundListAdapter = new FoundListAdapter(this, listItems);
lvDevices.setAdapter(foundListAdapter);
lvDevices.setOnItemClickListener(this);
btnSearch.setOnClickListener(this);
IntentFilter discoveryIntentFilter = new IntentFilter();
discoveryIntentFilter.addAction(BluetoothDevice.ACTION_FOUND);
discoveryIntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(discoveryBroadcastReceiver, discoveryIntentFilter);
startScan();
}
@Override
public void onClick(View view) {
if (view.equals(btnSearch)){
startScan();
}
}
public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
stopScan();
if (parent.equals(lvDevices)) {
BluetoothDevice device = listItems.get(position);
if (device != null) {
Log.d(TAG, "onItemClick: Imagine u connecting to:" + device.getName());
Intent intent = new Intent(this, DeviceControlActivity.class);
intent.putExtra("address", device.getAddress());
startActivity(intent);
}
}
}
@SuppressLint("StaticFieldLeak") // AsyncTask needs reference to this fragment
public void startScan() {
checkLocationPermission();
checkBluetoothState();
if(scanState != ScanState.NONE)
return;
scanState = ScanState.LESCAN;
if(!locationEnabled)
scanState = ScanState.DISCOVERY;
// Starting with Android 6.0 a bluetooth scan requires ACCESS_COARSE_LOCATION permission, but that's not all!
// LESCAN also needs enabled 'location services', whereas DISCOVERY works without.
// Most users think of GPS as 'location service', but it includes more, as we see here.
// Instead of asking the user to enable something they consider unrelated,
// we fall back to the older API that scans for bluetooth classic _and_ LE
// sometimes the older API returns less results or slower
listItems.clear();
foundListAdapter.notifyDataSetChanged();
tvNoDeviceFound.setVisibility(View.INVISIBLE);
if(scanState == ScanState.LESCAN) {
leScanStopHandler.postDelayed(this::stopScan, LESCAN_PERIOD);
pbSearch.setVisibility(View.VISIBLE);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void[] params) {
bluetoothAdapter.startLeScan(null,leScanCallback);
return null;
}
}.execute();// start async to prevent blocking UI, because startLeScan sometimes take some seconds
} else {
bluetoothAdapter.startDiscovery();
}
}
private void updateScan(BluetoothDevice device, int rssi) {
if(scanState == ScanState.NONE)
return;
if(listItems.indexOf(device) < 0 && device.getName() != null) {
listItems.add(device);
Log.d(TAG, "Device found: " + device.getName());
Collections.sort(listItems, DevicesFragment::compareTo);
foundListAdapter.notifyDataSetChanged();
}
}
private void stopScan() {
pbSearch.setVisibility(View.INVISIBLE);
if(scanState == ScanState.NONE)
return;
if (listItems.size() == 0) {
tvNoDeviceFound.setVisibility(View.VISIBLE);
}
switch(scanState) {
case LESCAN:
leScanStopHandler.removeCallbacks(this::stopScan);
bluetoothAdapter.stopLeScan(leScanCallback);
break;
case DISCOVERY:
bluetoothAdapter.cancelDiscovery();
break;
default:
// already canceled
}
scanState = ScanState.NONE;
}
BroadcastReceiver discoveryBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
if(device.getName() != null && device.getType() != BluetoothDevice.DEVICE_TYPE_CLASSIC) {
updateScan(device, rssi);
}
}
if(intent.getAction().equals((BluetoothAdapter.ACTION_DISCOVERY_FINISHED))) {
scanState = ScanState.DISCOVERY_FINISHED; // don't cancel again
stopScan();
}
}
};
private void checkBluetoothState() {
if (this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
private void checkLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
scanState = ScanState.NONE;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.location_permission_title);
builder.setMessage(R.string.location_permission_message);
builder.setPositiveButton(android.R.string.ok,
(dialog, which) -> requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 0));
builder.show();
return;
}
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); //this, was getActivity()
locationEnabled = false;
try {
locationEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
Log.d(TAG, "checkLocationPermission: GPS PROVIDER GRANTED");
return;
} catch(Exception ignored) {}
try {
locationEnabled |= locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
Log.d(TAG, "checkLocationPermission: NETWORK PROVIDER GRANTED");
return;
} catch(Exception ignored) {}
}
Log.d(TAG, "checkLocationPermission: PERMISSIONS DENIED");
return;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// ignore requestCode as there is only one in this fragment
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
new Handler(Looper.getMainLooper()).postDelayed(this::startScan,1); // run after onResume to avoid wrong empty-text
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !shouldShowRequestPermissionRationale(permissions[0])) {
Toast.makeText(getApplicationContext(), "Application requires LOCATION PERMISSIONS GRANTED to work.", Toast.LENGTH_LONG).show();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Приложению требуются разрешения");
builder.setMessage("Для работы Bluetooth, приложению необходимо получить разрешение на геолокацию");
builder.setPositiveButton("Перейти к настройкам приложения", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
builder.show();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getText(R.string.location_denied_title));
builder.setMessage(getText(R.string.location_denied_message));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
}