Android Bluetooth Low Energy Callback(LeScanCallBack) on seperate thread? - android

I'm starting the Bluetooth Low Energy Scan in the MainActivity:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BLEScanner.start(bluetoothManager.getAdapter());
}
}
The (static) BLEScanner class is as follows:
public class BLEScanner {
public static void start(final BluetoothAdapter bluetoothAdapter) {
bluetoothAdapter.startLeScan(mLeScanCallback);
}
private static BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
String name = device.getName();
String address = device.getAddress();
Log.d("BLESCANNER", name+" "+address);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
The Thread.sleep() in BLEScanner causes the UIThread to be unresponsive. How can i run the BLEScanner class in a seperate class?

private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
new Thread(new Runnable() {
#Override
public void run() {
//The code here is executed on on new thread everytime
Log.e("LeScanCallback", Thread.currentThread().getName());//Prints Thread-xxxx
}
}).start();
}
};

Related

Why MainActivity is declared static inside other Class?

I followed a tutorial on Bluetooth Low Energy, and I have started to add my own menus, etc.
The question I have is, why would MainActivity be static and put inside another class.
The full tutorial is available on Github: https://github.com/kaviles/BLE_Tutorials
Below is the Scanner_BTLE class that contains static MainActivity.
public class Scanner_BTLE {
private MainActivity ma;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private long scanPeriod;
private int signalStrength;
private UUID[] uuids;
// ToDO add Runtime Permissions to activate it momentarily once user asks for scanning
// ToDo this is currently done in App permitions while in dedug mode, add the software in the code so //permisiions does not have to be done on phone
// ToDo see "https://andela.com/insights/how-to-scan-for-android-bluetooth-low-energy-devices-successfully/"
// ToDO Look at "B- or add Runtime Permissions..... " on how to add it in code
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public Scanner_BTLE(Scanner_BTLE.MainActivity mainActivity, long scanPeriod, int signalStrength) {
ma = mainActivity;
mHandler = new Handler();
this.scanPeriod = scanPeriod;
this.signalStrength = signalStrength;
final BluetoothManager bluetoothManager =
(BluetoothManager) ma.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
public boolean isScanning() {
return mScanning;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void start() {
if (!Utils.checkBluetooth(mBluetoothAdapter)) {
Utils.requestUserBluetooth(ma);
ma.stopScan();
}
else {
scanLeDevice(true);
}
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void stop() {
scanLeDevice(false);
}
//ToDo Use the Callback to find only the sps UUID of Relay
// If you want to scan for only specific types of peripherals,
// you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback),
// providing an array of UUID objects that specify the GATT services your app supports.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
if (enable && !mScanning) {
Utils.toast(ma.getApplicationContext(), "Starting BLE scan...");
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Utils.toast(ma.getApplicationContext(), "Stopping BLE scan...");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
ma.stopScan();
}
}, scanPeriod);
mScanning = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
}
else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
final int new_rssi = rssi;
if (rssi > signalStrength) {
mHandler.post(new Runnable() {
#Override
public void run() {
ma.addDevice(device, new_rssi);
}
});
}
}
};
public static class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener {
private final static String TAG = MainActivity.class.getSimpleName();
public static final int REQUEST_ENABLE_BT = 1;
public static final int BTLE_SERVICES = 2;
private HashMap<String, BTLE_Device> mBTDevicesHashMap;
private ArrayList<BTLE_Device> mBTDevicesArrayList;
private ListAdapter_BTLE_Devices adapter;
private ListView listView;
private Button btn_Scan;
private BroadcastReceiver_BTState mBTStateUpdateReceiver;
private Scanner_BTLE mBTLeScanner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Utils.toast(getApplicationContext(), "BLE not supported");
finish();
}
mBTStateUpdateReceiver = new BroadcastReceiver_BTState(getApplicationContext());
mBTLeScanner = new Scanner_BTLE(this, 5000, -75);
mBTDevicesHashMap = new HashMap<>();
mBTDevicesArrayList = new ArrayList<>();
adapter = new ListAdapter_BTLE_Devices(this, R.layout.btle_device_list_item, mBTDevicesArrayList);
listView = new ListView(this);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
btn_Scan = (Button) findViewById(R.id.btn_scan);
((ScrollView) findViewById(R.id.scrollView)).addView(listView);
findViewById(R.id.btn_scan).setOnClickListener(this);
}
#Override
protected void onStart() {
super.onStart();
registerReceiver(mBTStateUpdateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
#Override
protected void onResume() {
super.onResume();
// registerReceiver(mBTStateUpdateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
}
#Override
protected void onPause() {
super.onPause();
// unregisterReceiver(mBTStateUpdateReceiver);
stopScan();
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(mBTStateUpdateReceiver);
stopScan();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REQUEST_ENABLE_BT) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// Utils.toast(getApplicationContext(), "Thank you for turning on Bluetooth");
}
else if (resultCode == RESULT_CANCELED) {
Utils.toast(getApplicationContext(), "Please turn on Bluetooth");
}
}
else if (requestCode == BTLE_SERVICES) {
// Do something
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Context context = view.getContext();
// Utils.toast(context, "List Item clicked");
// do something with the text views and start the next activity.
stopScan();
String name = mBTDevicesArrayList.get(position).getName();
String address = mBTDevicesArrayList.get(position).getAddress();
Intent intent = new Intent(this, Activity_BTLE_Services.class);
intent.putExtra(Activity_BTLE_Services.EXTRA_NAME, name);
intent.putExtra(Activity_BTLE_Services.EXTRA_ADDRESS, address);
startActivityForResult(intent, BTLE_SERVICES);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_scan:
Utils.toast(getApplicationContext(), "Scan Button Pressed");
if (!mBTLeScanner.isScanning()) {
startScan();
}
else {
stopScan();
}
break;
default:
break;
}
}
public void addDevice(BluetoothDevice device, int rssi) {
String address = device.getAddress();
if (!mBTDevicesHashMap.containsKey(address)) {
BTLE_Device btleDevice = new BTLE_Device(device);
btleDevice.setRSSI(rssi);
mBTDevicesHashMap.put(address, btleDevice);
mBTDevicesArrayList.add(btleDevice);
}
else {
mBTDevicesHashMap.get(address).setRSSI(rssi);
}
adapter.notifyDataSetChanged();
}
public void startScan(){
btn_Scan.setText("Scanning...");
mBTDevicesArrayList.clear();
mBTDevicesHashMap.clear();
mBTLeScanner.start();
}
public void stopScan() {
btn_Scan.setText("Scan Again");
mBTLeScanner.stop();
}
}
}

Unable to write to Arduino from Android using BLE

I'm having a problem. I am attempting to write a value to my Arduino from my Android smartphone over BLE. My board is a Genuino 101, so I am using CurieBLE. I am able to access the correct service and characteristic, but I always get a false value returned from writeCharacteristic. The onCharacteristicWrite function is also not being called.
Here is the code for my Bluetooth class:
public class BluetoothActivity extends AppCompatActivity{
private BluetoothAdapter btAdapt = BluetoothAdapter.getDefaultAdapter();
private boolean mScan;
private BluetoothGatt mGatt;
private Handler handler = new Handler();
private static final long SCAN_TIME = 10000;
private static final String DEVICE_ADDRESS = "98:4F:EE:0F:CB:69";
private static final String LIGHT_SERVICE = "19B10000-E8F2-537E-4F6C-D104768A1214";
private Context context = this;
private BluetoothGattCharacteristic sendVal;
/**
* Function to scan device
* #param enable: Switch to begin scanning
*/
public void ScanDevice(final boolean enable){
if(enable){
handler.postDelayed(new Runnable() {
#Override
public void run() {
mScan = false;
btAdapt.stopLeScan(mCallback);
}
}, SCAN_TIME);
mScan = true;
btAdapt.startLeScan(mCallback);
}else{
mScan = false;
btAdapt.stopLeScan(mCallback);
}
}
/**
* Callback to execute upon finding device.
*/
private BluetoothAdapter.LeScanCallback mCallback = new BluetoothAdapter.LeScanCallback() {
#Override
//!Function to connect to device
public void onLeScan(final BluetoothDevice device, int i, byte[] bytes) {
String address = device.getAddress();
if(address.equals(DEVICE_ADDRESS)) {
mGatt = device.connectGatt(context, false, bCallback);
Log.i("BTConnect", "Found it!");
}
else{
Log.i("BTConnect", "Wrong device!");
}
}
};
/**
* Callback to find services of device.
*/
private final BluetoothGattCallback bCallback = new BluetoothGattCallback() {
//!Function to discover services
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
gatt.discoverServices();
}
//!Function to deal with discovered services
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mGatt = gatt;
List<BluetoothGattService> bluetoothGattServiceList= mGatt.getServices();
BluetoothGattService LightUp = bluetoothGattServiceList.get(findService(bluetoothGattServiceList));
List<BluetoothGattCharacteristic> bluetoothGattCharacteristicList = LightUp.getCharacteristics();
sendVal = bluetoothGattCharacteristicList.get(findCharacteristic(bluetoothGattCharacteristicList));
}
//!Function to deal with characteristic writes
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS){
Log.i("BTWrite","Write to Characteristic Success!");
}else{
Log.i("BTWrite","Blast!Foiled!");
}
}
};
//!Function to find the right service
private int findService(List<BluetoothGattService> list){
int index = 0;
for(int i = 0; i<list.size(); i++){
if(list.get(i).getUuid().equals(LIGHT_SERVICE)){
index = i;break;}
}
return index;
}
//!Function to find the right characteristic
private int findCharacteristic(List<BluetoothGattCharacteristic> list){
int index = 0;
for(int i = 0; i<list.size(); i++){
if(list.get(i).getUuid().equals(LIGHT_SERVICE)){
index = i;break;}
}
return index;
}
//!Function to actually write to Arduino
public boolean WriteVal(){
if(sendVal==null){
Log.i("BTWrite", "Disconnected!");
return false;
}
byte[] value = new byte[1];
value[0] = (byte) (Math.random() * 126);
sendVal.setValue(value);
boolean ans = mGatt.writeCharacteristic(sendVal);
return ans;
}
}
And here is the main class which contains the UI:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter btAdapt = BluetoothAdapter.getDefaultAdapter();
BluetoothActivity btActivity = new BluetoothActivity();
/**
* Main function for opening screen.
* #param savedInstanceState: Instance for creation
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//!Open the layout
if(btAdapt.isEnabled()){
Intent enableBt = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBt, REQUEST_ENABLE_BT);
}
findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);//!Set listener to button
}
#Override
public void onClick(View v){
switch(v.getId()) {
case R.id.button1:
btActivity.ScanDevice(true);break;//!Scan for shisha
case R.id.button2:
btActivity.WriteVal();break;//!Write to shisha
}
}
}
I appreciate all your help. Thank you in advance.
Your infinite do-while loop doesn't look so good (why is it there?). Since having that means you won't return from the onServicesDiscovered method, and therefore you block the onCharacteristicWrite callback from running (it gets enqueued). Also note that in Android you may only have one outstanding GATT operation per BluetoothGatt object (you must wait until the corresponding callback arrives before you can issue another operation). That is the reason for your writeCharacteristic calls return false.

Android Bluetooth Low Energy Not Finding Devices

I am trying to set up a basic Android App that will have Bluetooth LE functionality as per the documentation on the Android Developers site. I successfully can get to the point where I am starting a scan, but it is not finding any of the peripherals I have been using to test. Here is the code as it is:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Bind(R.id.button) Button mButton;
#Bind(R.id.listView) ListView mListView;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning = true;
private Handler mHandler;
private static final long SCAN_PERIOD = 10000;
BluetoothLeScanner mBluetoothAdapterBluetoothLeScanner;
ArrayList<String> bluetoothArray = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mButton.setOnClickListener(this);
Context mContext = getBaseContext();
final BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
mBluetoothAdapterBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1900);
}
}
#Override
public void onClick(View view) {
scanLeDevice(mScanning);
}
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);
}
}
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() {
Log.d("test" ,device.getName());
}
});
}
};
}
Do you know exactly what the advertising data content is? Maybe it's blocked by the bluetooth stack(bluetooth.default.so) .For example, if the advertising device has not set the "discoverable bit" in AD flag, it will be blocked and you will not receive the report in your callback function.

Bluetooth develop:sometimes the bluetoothadapter cannot find the device

I'm working at BLE develop and there is my problem.
Sometimes my bluetoothadapter cannot find the device when I used method bluetoothAdapter.startLeScan(callback).
And when adapter cannot found device,I just restart bluetoothadapter(meant:turn off the bluetoothadapter then turn on).
And then the callback will return the device instead null.
I have search much about BLE develop ,but it seems like nobody found this problem.
This is my 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() {
synchronized (m_LeDevices) {
if (m_device_mac != null && m_device_mac.equals(device.getAddress().replace(":", ""))) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
forwardToMain();
}
}, DELAY_FORWARD_TIME);
}
}
}
});
}
};

how to do continuous starting and stopping of scanning ble in android

For continuous scanning of advertising data, i tried in starting and stopping the mBluetoothAdapter.stopLeScan(mLeScanCallback) function ,but still it is happening only once .when i tried to call the scanning twice, in logcat i can see that LE scan has already started ,as the second call is not executing . how to call repeated scanning and stopping here .
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);
System.out.println("hello");
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
System.out.println(SCAN_PERIOD);
mScanning = true;
System.out.println("starttt 1!");
mBluetoothAdapter.startLeScan(mLeScanCallback);
//////
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
System.out.println("hello");
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
System.out.println(SCAN_PERIOD);
mScanning = true;
System.out.println("starttt 1!");
mBluetoothAdapter.startLeScan(mLeScanCallback);
//////////
}
else
{
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
This works pretty well for me,
I have a method called discoverBLEdevices, which is called in onCreate.
#SuppressLint("NewApi")
private void discoverBLEDevices() {
// TODO Auto-generated method stub
startScan.run();
Log.e("BLE_Scanner", "DiscoverBLE");
}
I have a startScan Runnable, in which I start the scan and invoke another runnable to stop the scan as shown below :
private Runnable startScan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, 500);
mBLEAdapter.startLeScan(mLeScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
After 500 msec I invoke a runnable called stopScan which stops the scanning and calls startScan runnable again.
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mBLEAdapter.stopLeScan(mLeScanCallback);
scanHandler.postDelayed(startScan, 10);
Log.e("BLE_Scanner", "Stop Scan");
}
};
The callback which I have is as follows :
// Device scan callback.
#SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#SuppressLint("NewApi")
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
String Address = device.getAddress();
String Name = device.getName();
Log.e("mLeScanCallback",""+Address +" : "+Name);
}
};
The entire code :
import android.annotation.SuppressLint;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
#SuppressLint("NewApi")
public class BluetoothLE extends Service {
private BluetoothAdapter mBLEAdapter;
private BluetoothManager manager;
private Handler scanHandler = new Handler();
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
turnonBLE();
discoverBLEDevices();
}
#SuppressLint("NewApi")
private void turnonBLE() {
// TODO Auto-generated method stub
manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBLEAdapter = manager.getAdapter();
mBLEAdapter.enable();
Toast.makeText(getApplicationContext(), "BTLE ON Service",
Toast.LENGTH_LONG).show();
Log.e("BLE_Scanner", "TurnOnBLE");}
#SuppressLint("NewApi")
private void discoverBLEDevices() {
// TODO Auto-generated method stub
startScan.run();
Log.e("BLE_Scanner", "DiscoverBLE");
}
private Runnable startScan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, 500);
mBLEAdapter.startLeScan(mLeScanCallback);
}
};
// Device scan callback.
#SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#SuppressLint("NewApi")
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
String Address = device.getAddress();
String Name = device.getName();
Log.e("mLeScanCallback",""+Address +" : "+Name);
}
};
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mBLEAdapter.stopLeScan(mLeScanCallback);
scanHandler.postDelayed(startScan, 10);
}
};
}
In case it doesn't update, I added a few lines by turning on/turning off the Bluetooth adapter. Works fine in my case.
fun discoverBLEDevices(){
startScan.run()
Log.d("Scan", " Discover BLE")
}
private val startScan:Runnable = Runnable {
scanHandler.postDelayed(stopScan,10000)
bluetoothLeScanner.startScan(bleScanner)
// bluetoothAdapter.enable()
Log.e("BLE_Scanner", "Start Scan")
}
private val stopScan:Runnable = Runnable {
bluetoothLeScanner.stopScan(bleScanner)
bluetoothAdapter.disable()
bluetoothAdapter.enable()
scanHandler.postDelayed(startScan, 1000)
Log.e("BLE_Scanner", "Stop Scan")
}

Categories

Resources