I'm writing an Android-App, which is supposed to discover devices via Bluetooth.
I don't get any exceptions, but the devices are just not found, even though my windows pc finds them (and can be found itself).
I'm certain they are BLE, but I tried both ways. And of course I tried them separately.
Here is my ListActivity, which searches for the devices:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.bluetooth_list_view);
listView = getListView();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE is not supported", Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
Log.d(TAG, "on Create start5");
if (mBluetoothAdapter == null) {
Toast.makeText(this, "BLE is not supported", Toast.LENGTH_SHORT).show();
finish();
return;
}
mLeDeviceListAdapter = new LeDeviceListAdapter(this);
setListAdapter(mLeDeviceListAdapter);
listView.setAdapter(mLeDeviceListAdapter);
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
mBluetoothAdapter.startDiscovery();
}
private void scanLeDevice(final boolean enable) {
Log.e(TAG, "scanLeDevice: " + enable);
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.e(TAG, "after Scan mLeDeviceListAdapter: " + mLeDeviceListAdapter.getCount());
Log.e(TAG, "after Scan isEmpty(): " + mLeDeviceListAdapter.isEmpty());
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothLeScanner.startScan(mScanCallback);
} else {
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
}
invalidateOptionsMenu();
}
The logs say after Scan mLeDeviceListAdapter: 0 and after Scan isEmpty(): true.
ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.e(TAG, "onScanResult: " + result);
super.onScanResult(callbackType, result);
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
};
That log (onScanResult) is never called.
And for not BLE:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { //TODO not ble example
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
Log.e(TAG, "Not BLE discovry starts");
//discovery starts, we can show progress dialog or perform other tasks
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.e(TAG, "Not BLE discovry finishes");
//discovery finishes, dismis progress dialog
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//bluetooth device found
BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.e(TAG, "Found device " + device.getName());
}
}
};
Not BLE discovry finishes is printed and so is Not BLE discovry starts. Found device is never printed. But I'm positiv that the devices are BLE.
I have the necessary permissions in my manifest:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
What am I doing wrong?
Edit:
i really need help. Thinks I tried/checked so far:
Enabled Bluetooth adapter.
Bluetooth usage permissions granted.
Location permission (ACCESS_COARSE and ACCESS_FINE) granted.
Made sure gps provider is enabled (Smartphone GPS settings).
Maybe because you didn't request ACCESS_COARSE_LOCATION in onCreate
This project is a good resource for you to start your Bluetooth scanner:
https://github.com/joelwass/Android-BLE-Scan-Example
Related
private UUID[] RX_SERVICE_UUID = new UUID[1];
RX_SERVICE_UUID[0] = UUID.fromString("0000e4eee-1112-1111-1111-14addb4as215");
when i am using the function with UUID mBluetoothAdapter.startLeScan(RX_SERVICE_UUID,mLeScanCallback) method. It is not scanning the device when i am using without UUID means mBluetoothAdapter.startLeScan(mLeScanCallback) then it is scanning the device. Please suggest.
I think you are testing app in marshmallow
for scaning any ble device required some permission
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" />
enable bluetooth
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter();
mLeDevicePairedListAdapter = new LeDevicePairedListAdapter();
setListAdapter(mLeDeviceListAdapter);
setPairedListAdapter(mLeDevicePairedListAdapter);
scanLeDevice(true);
start scaning by using this method
private void scanLeDevice(final boolean enable) {
if (enable) {
mScanning = true;
selectedPositions = new ArrayList<>();
pairselectedPositions = new ArrayList<>();
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
// invalidateOptionsMenu();
}
callbackresponse
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
//if device bounded then added another list and not bound then using other list diplay in list
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
mLeDevicePairedListAdapter.addDevice(device, rssi);
} else {
mLeDeviceListAdapter.addDevice(device, rssi);
}
}
});
}
};
I have combed the forums in order to try and figure out why my bluetooth BroadcastReceiver isn't working and all I can find is people saying to ensure that coarse/fine location permissions are requested. I'm already doing that here (method is called before the discovery method):
Request Permissions
private void requestPermissions()
{
final int CODE = 5; // app defined constant used for onRequestPermissionsResult
boolean allPermissionsGranted = true;
String[] permissionsToRequest =
{
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
for(String permission : permissionsToRequest)
{
allPermissionsGranted = allPermissionsGranted &&
(ContextCompat.checkSelfPermission(this, permission)
== PackageManager.PERMISSION_GRANTED);
}
if(!allPermissionsGranted)
{
ActivityCompat.requestPermissions(this, permissionsToRequest, CODE);
}
}
The problem is that my BroadcastReceiver never finds other discoverable devices, if a device is already paired, it puts it in a ListView as expected, but once the device is unpaired, it cannot identify it again. See screenshots below.
Output with no paired devices
Output with paired devices
I'm at a loss at this point, it feels like I've tried everything, if anyone has any ideas you'd save my life and I'd be eternally grateful.
Thanks,
Broadcast Receiver
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action))
{
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(btDevice.getBondState() != BluetoothDevice.BOND_BONDED)
{
newDevicesArrayAdapter.add(btDevice.getName() + "\n" + btDevice.getAddress());
}
}
else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action))
{
setTitle(R.string.select_device);
if(newDevicesArrayAdapter.getCount() == 0)
{
newDevicesArrayAdapter.clear();
String noDevices = getResources().getText(R.string.none_found).toString();
newDevicesArrayAdapter.add(noDevices);
}
}
}
};
onCreate - Setup ListView elements and IntentFilters
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_machine);
setResult(Activity.RESULT_CANCELED);
pairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
newDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
pairedListView.setAdapter(pairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener(deviceClickListener);
newDevicesListView.setAdapter(pairedDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(deviceClickListener);
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(broadcastReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(broadcastReceiver, filter);
}
doDiscovery() - Called by onStart method, I've verified that it gets called
private void doDiscovery()
{
Log.d(TAG, "doDiscovery()");
requestPermissions();
setTitle(R.string.scanning);
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
if(btAdapter.isDiscovering()) { btAdapter.cancelDiscovery(); }
newDevicesArrayAdapter.clear();
btAdapter.startDiscovery();
}
Turns out I was just being stupid and had values updating to both ListViews, so I never saw the list of other devices.
newDevicesListView.setAdapter(pairedDevicesArrayAdapter);
Should have been:
newDevicesListView.setAdapter(newDevicesArrayAdapter);
I have a dialog that scans for BLE devices for 10 seconds. When I start my scan I enable a spinner at the footer of the list. When the scan is completed I'd like to remove that spinner. I'm trying to get this to work with the deprecated mBluetoothAdapter.stopLeScan(callback) function instead of the new startScan/stopScan functions as if the device isn't running version 21 or higher, you have to fallback to this method.
stopLeScan requires the same callback as startLeScan but I dont think I see the callback being made. I was hoping that it was a simple check to see if the BluetoohDevice was null, then the callback was made because the scan was stopped, but this didn't work.
With the old version of the SDK, how do you get when the scan has been stopped (either due to the proper device being found or the scan time completed)? I could pass another handler to the my scanLeDevice function, but that just seems silly as I'm already passing a callback.
Bluetooth scanner
public class BleDevice {
private final static String TAG = BleDevice.class.getSimpleName();
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
public BleDevice() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mHandler = new Handler();
}
public void scanLeDevice(final boolean enable, final BluetoothAdapter.LeScanCallback callback) {
if (enable == true && mScanning == false) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// Turn off scanning
scanLeDevice(false, callback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(callback);
Log.d(TAG, "Starting Bluetooth LE scan");
} else if(enable == false && mScanning == true) {
mScanning = false;
mBluetoothAdapter.stopLeScan(callback);
Log.d(TAG, "Stopped Bluetooth LE scan");
}
}
}
Callback in Dialog Box:
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.d(TAG, device.getAddress() + " " + device.getName() + "");
if(device == null) {
Log.d(TAG, "Device is null? stop?");
} else {
btAdapter.add(device);
}
}
};
In mBluetoothAdapter.stopLeScan(callback); the callback is just used to identify which scan is to be stopped and it's not supposed to trigger any method in the callback. It's a synchronous operation.
And the BluetoothAdapter.LeScanCallback class doesn't even have any methods beyond the onLeScan() which just receives the results.
So, you can define your own method to be triggered when the scan is stopped by you:
...
} else if(enable == false && mScanning == true) {
mScanning = false;
mBluetoothAdapter.stopLeScan(callback);
Log.d(TAG, "Stopped Bluetooth LE scan");
onScanStopped(); // <--- Remove the spinner here.
}
I'm not aware of any automatic timeout for startLeScan(), so as far as I know it should only stop by calling stopLeScan(). And onLeScan() being triggered doesn't stop the scan either.
I can`t run startDiscovery (for BlueTooth) from Service.
Service run from sleep by WakefulBroadcastReceiver (by timer).
Source code of Service:
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private static final String TAG = "LocationService";
private BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service onCreate");
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mBTReceiver, filter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service onStartCommand");
BTscanner();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (btAdapter != null) {
btAdapter.cancelDiscovery();
}
unregisterReceiver(mBTReceiver);
}
private void BTscanner() {
Log.e(TAG, "==BT: Run BTscanner");
btAdapter.cancelDiscovery();
btAdapter.startDiscovery();
Log.e(TAG, "==BT: End BTscanner");
}
private final BroadcastReceiver mBTReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
Log.e(TAG, "==BT: Started");
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
Log.e(TAG, "==BT: Finished");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.e(TAG, "==BT: " + device.getAddress());
}
}
};
}
In log i see:
Service onStartCommand
==BT: Run BTscanner
==BT: End BTscanner
but don`t see:
==BT: Started
==BT: Finished
and list of discovered devices.
In Manifest all permissions installed:
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
and service enabled in application field of Manifest:
<service
android:name=".LocationService"
android:enabled="true"
android:exported="true" />
What did I do wrong?
Tnx.
Are you sure that your receiver has not been unregistered in the onDestroy method before having the time to handle any action? I would put a break point in it to see what's happenning in there?
I think, I found a solution.
There were problems when I install and run the application with active Bluetooth on smartphone.
After installing application and manual deactivate/activate Bluetooth device all works fine.
Maybe it was some sort of a hardware failure.
Then reinstall the application twice, and the problem is not repeated.
I need to get a list of available bluetooth devices in the area using google android 2.1.
Thing is, i don't just need a list of those devices, i need some unique id for each device found and i need an indicator, how "good" the signal is received (like the "level" in android.wifi.ScanResult)... How do i do that?
Check out code below :
Starting search
mBluetoothAdapter.startDiscovery();
mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//Finding devices
if (BluetoothDevice.ACTION_FOUND.equals(action))
{
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
};
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
Call method bluetoothScanning, context is required
void bluetoothScanning(){
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
context.registerReceiver(mReceiver, filter);
final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.startDiscovery();
}
// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
Log.i("Device Name: " , "device " + deviceName);
Log.i("deviceHardwareAddress " , "hard" + deviceHardwareAddress);
}
}
};
Result
Name: LE-Bose Revolve+ SoundLink
deviceHardwareAddress: MAC .....
This code uses BeaconManager, it continuously scans for new Bluetooth devices and returns a Beacons List object which you can use to get what ever information you need.
Make sure you import BeaconManager
private BeaconManager beaconManager;
//In onCreate method
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
//use these out of the onCreate method
public void onScanStart(View view) {
stopScanButton.setEnabled(true);
scanningButton.setEnabled(false);
beaconManager.bind(this);
}
#Override
public void onBeaconServiceConnect() {
beaconManager.removeAllRangeNotifiers();
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon b : beacons) {
System.out.println(String.format("%s: %f: %d", b.getBluetoothName(), b.getDistance(), b.getRssi()));
});
try {
//Tells the BeaconService to start looking for beacons that match the passed.
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
}
}
Let me know if that works for you!
To able to discovery devices by bluetooth. Make sure you
Enable bluetooth
Allow required permissions for your application (some permission is runtime permission). You can check here https://developer.android.com/about/versions/12/features/bluetooth-permissions
AndroidManifest.xml
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
MainActivity
class MainActivity : AppCompatActivity() {
private var bluetoothAdapter: BluetoothAdapter? = null
private val bluetoothReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
Log.i("TAG", "onReceive $action")
if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) {
Log.i("TAG", "Discovery finished, hide loading")
} else if (BluetoothDevice.ACTION_FOUND == action) {
val device =
intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
Log.i("TAG", "Device Name: " + (device?.name ?: ""))
Log.i("TAG", "Device Address:" + (device?.address ?: ""))
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
findViewById<Button>(R.id.button_start_discovery).setOnClickListener {
if (bluetoothAdapter == null) {
initBluetoothDiscovery()
}
startDiscovery()
}
}
private fun initBluetoothDiscovery() {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
val intentFilter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_FOUND)
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
}
registerReceiver(bluetoothReceiver, intentFilter)
}
private fun startDiscovery() {
if (bluetoothAdapter?.isDiscovering == true) {
Log.i("TAG", "cancel start discovery")
bluetoothAdapter?.cancelDiscovery()
}
Log.i("TAG", "start discovery, show loading")
bluetoothAdapter?.startDiscovery()
}
override fun onDestroy() {
super.onDestroy()
bluetoothAdapter?.cancelDiscovery();
unregisterReceiver(bluetoothReceiver);
}
}