I want to analysis the heart rate of a heart rate monitor. For that I want to save the last used device and compare it to the found devices. Because it takes a while to find devices, mDevice remains null. What do I have to do to update mDevice properly?
private ArrayList<BluetoothDevice> mDeviceList;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private BluetoothDevice mDevice;
private static final int REQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
#Override
protected void onStart() {
super.onStart();
// 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()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
// Initializes list view adapter.
mDeviceList = new ArrayList<BluetoothDevice>();
scanLeDevice(true);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final String adress = prefs.getString(getString(R.string.device_address), "");
for(BluetoothDevice b : mDeviceList){
if(b.getAddress().equals(adress)){
mDevice = b;
}
}
if(mDevice != null)
Log.e(TAG, mDevice.getAddress());
}
taken from the google manual:
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!mDeviceList.contains(device)){
mDeviceList.add(device);
}
}
});
}
};
I hope these are enough information. If something is missing, feel free to ask
The scan is a background activity your trying to look at the results straight after you start it rather than waiting for it to finish. You may want to put your checking code into the onLeScan call-back directly and stopping the scan as soon as you see the device you want.
You could also try not doing the scan all together if you already have the details for the device just try going straight to connect. The details of if you need to scan before you connect are not at all clear from the documentation so you need to be prepared to experiment a bit as it's still all far too flaky.
Just move your code where you try to find the last device (everything in onStart() after SharedPreferences prefs...) to after you have already found devices, for example at the end of your runnable (after invalidateOptionsMenu();)
Related
I have faced with the issue using startScan method of BluetoothLeScanner a BLE device was found, but when I turned off BLE device my phone still shows this device as turned on !!
I have tried to use:
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("ScanCallback", String.format("onScanResult(int callbackType[%d], ScanResult result)", callbackType));
final BluetoothDevice btDevice = result.getDevice();
if (btDevice == null){
Log.e("ScanCallback", "Could not get bluetooth device");
return;
}
final String macAddress = btDevice.getAddress();
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
// NOTE: I've never got here
final BluetoothDevice outOfRangeDevice = mBtDevices.get(macAddress);
...
} else {
...
}
}
...
};
Guy, I have not found solution how to detect that BLE device is lost in other resources like (Android SDK reference, forums, stackoverflow and etc) (:
Any help will be appreciated !!
During googling and exploring the Android Documentations I have figured out how to detect if device is out of range. I would like to share my solution how I did it:
...
public void scanBLEDevices(final boolean enable) {
if(mLeScanner == null) {
Log.d(TAG, "Could not get LEScanner object");
throw new InternalError("Could not get LEScanner object");
}
if (enable) {
startLeScan();
} else {
stopLeScan(false);
}
}
private void startLeScan() {
Log.i(TAG, "startLeScan(BluetoothLeScanner mLeScanner)");
mScanning = true;
mInRangeBtDevices.clear();
if (mStartScanCallback != null) {
mHandler.removeCallbacks(mStartScanCallback);
}
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLeScanner.startScan(mScanFilters, mScanSettings, mScanCallback);
}
mStopScanCallback = new Runnable() {
#Override
public void run() {
stopLeScan(true);
}
};
mHandler.postDelayed(mStopScanCallback, SCAN_PERIOD);
}
private void stopLeScan(final boolean isContinueAfterPause) {
Log.i(TAG, "stopLeScan(BluetoothLeScanner mLeScanner)");
mScanning = false;
if (mStopScanCallback != null) {
mHandler.removeCallbacks(mStopScanCallback);
}
removeOutOfRangeDevices();
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLeScanner.stopScan(mScanCallback);
}
if (isContinueAfterPause) {
mStartScanCallback = new Runnable() {
#Override
public void run() {
startLeScan();
}
};
mHandler.postDelayed(mStartScanCallback, SCAN_PAUSE);
}
}
private void removeOutOfRangeDevices() {
final Set<String> outOfRangeDevices = new HashSet<>();
for (String btAddress : mBtDevices.keySet()) {
if (!mInRangeBtDevices.contains(btAddress)) {
outOfRangeDevices.add(btAddress);
}
}
for (String btAddress : outOfRangeDevices) {
final BluetoothDevice outOfRangeDevice = mBtDevices.get(btAddress);
mBtDevicesRSSI.remove(btAddress);
mBtDevices.remove(btAddress);
}
}
...
Explanation:
As you can see I have added on each scanning period mInRangeBtDevices collection that will keep all devices found during the current scanning.
When I stop scanning, I am also removing out of range device from previous lists that is not available anymore using one additional helper collection outOfRangeDevices
I think this example would be usefull and you will be able to integrate it in your own code
This one is looking good (JAVA):
As I understood, you need to implement startLeScan().
Find BLE devices
To find BLE devices, you use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:
As soon as you find the desired device, stop scanning.
Never scan on a loop, and set a time limit on your scan. A device that was previously available may have moved out of range, and continuing to scan drains the battery.
The following snippet shows how to start and stop a scan:
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...}
Consider checking this tutorial as well.
Also this one.
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 have tried this,
I am able to scan all devices but not able to calculate major and minor using Android core classes.
I have tried this
public class TagBluetooth {
private Context context;
private List uuidList;
private iTagBle iTagble;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;
private final int REQUEST_ENABLE_BT = 101;
private boolean mScanning;
private Handler handler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
private BluetoothAdapter.LeScanCallback leScanCallback;
private List list;
public TagBluetooth(iTagBle activity){
this.context= (Context) activity;
iTagble=activity;
}
public TagBluetooth(iTagBle activity,List list){
this.context= (Context) activity;
iTagble=activity;
uuidList=list;
}
/**
* initialize ble component
*/
public void initialize(){
if (isBLEAvailable()) {
bluetoothManager = (BluetoothManager) context.getSystemService(context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
handler = new Handler();
leScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
iTagble.onScanComplete(device,rssi,scanRecord);
}
};
}
checkBluetoothStatus();
}
/**
* Ensures Bluetooth is available on the device and it is enabled. If not,
* displays a dialog requesting user permission to enable Bluetooth.
*
* #return true for device bluetooth available and false for bluetooth not available
*/
private void checkBluetoothStatus() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
context.startActivity(enableBtIntent);
}
}
/**
* ebale and disable ble devices scanning
*
* #param enable
*/
public void scanLeDevice(final boolean enable) {
if(!isBLEAvailable())
return;
if (enable) {
// Stops scanning after a pre-defined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
bluetoothAdapter.startLeScan(leScanCallback);
} else {
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}
/**
* Use this check to determine whether BLE is supported on the device. Then
* you can selectively disable BLE-related features.
*
* #return true for BLE support and false for BLE unsupported
*/
private boolean isBLEAvailable() {
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
iTagble.showMessage("BLE not supported");
return false;
}
return true;
}
}
Requirement:
scan all beacon devices (including different manufacturer).
Track entry and exit from beacon.
Minor and Major ranging.
Scan device even application is closed.
If any one help me on this, it wold be grate for me.
Thanks in advance.
Use Kontakt API from here. But you can't major exact entry and exit time from any API. See here.
I am developing a demo app to scan Blutooth Le devices. But startLeScan() returns null. and I am not getting any device name. I have tried using normal scan it shows up fine. I am adding my snippet here
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mTxtInfo.setText("Stopped Scanning.");
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mTxtInfo.setText("Started Scanning...");
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mTxtInfo.setText("Stopped Scanning.");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
This is my function to start Le scan.
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mTxtInfo.setText("Detected devices: " + device.getName());
Toast.makeText(MainActivity.this,
"Detected devices: " + device.getName(),
Toast.LENGTH_LONG).show();
}
});
}
};
this is the callback. And its showing me
07-04 12:50:17.833: D/BluetoothAdapter(3564): startLeScan(): null
Would appreciate any help.
The Problem there is that you are trying to use
startLeScan(callback)
without setting the Uuid parameter. So the BluetoothAdapter code it's doing something like:
startLeScan(null, callback)
on the back and printing
"startLeScan:" + Uuid.
Which for you is null.
I am keep on getting the NoClassDefFoundError when my class is loaded.
The code is taken from BluetoothLeGatt project -
http://developer.android.com/samples/BluetoothLeGatt/project.html
My code:
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() { //java.lang.NoClassDefFoundError...
#Override
public void onLeScan(final BluetoothDevice device,
final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
String msg= device.getAddress();
Log.d(TAG,msg);
addItems(msg);
}
});
}
};
Someone suggested that the error is because my device doesn't support BLE but I want to get rid of this error for any device. So if it doesn't support BLE feature then simply skip this error else continue with the call to this BluetoothAdapter.LeScanCallback.
NOTE:
See this my previous SO post for more clarification.
Putting the BLE feature check as the first line onCreate() doesn't stop the crash --
#Override
public void onCreate(Bundle savedInstanceState) {
if (!bleCheck()) {
Toast.makeText(getApplicationContext(), R.string.ble_not_supported,
Toast.LENGTH_SHORT).show();
finish();
}
//Rest of the code
//Call to BLE Scan on button click that causes error..
}
private boolean bleCheck() {
boolean result = false;
if (getPackageManager().
hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
result = true;
}
return result;
}
As BluetoothAdapter.LeScanCallback was Added in API level 18 ; Source, this code would need a API level check also. Here's how i have gone about this, not declared the callback as private but under the condition:
boolean apiJBorAbove = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 ? true
: false;
boolean isBleAvailable = getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE) ? true : false;
// Use this check to determine whether BLE is supported on the device.
if (isBleAvailable && apiJBorAbove) {
// Initializes a Bluetooth adapter.
// For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Ensures Bluetooth is available on the device and it is enabled.
// If not, displays a dialog requesting user permission to enable
// Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
startActivity(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
}
BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device,
final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
String msg= device.getAddress();
// Log.d(TAG,msg);
// addItems(msg);
}
});
}
};
}
The NoClassDefFoundError is due to the API , not on the basis of PackageManager.FEATURE_BLUETOOTH_LE.