BLE scan does not discover device


BLE scan does not discover device



My code is as follows->



manifest


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.root.securityalert">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".DeviceScanActivity" android:label="Activity2" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:label="@string/app_name" android:name=".BlueTooth"/>
</application>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
<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" />
</manifest>



deviceScanActivity



class DeviceScanActivity extends AppCompatActivity/ListActivity/ {


public BluetoothAdapter mBluetoothAdapter;
public /*final*/ BluetoothManager mBluetoothManager;

ArrayList<ViewHolder> arrayOfUsers2 = new ArrayList<ViewHolder>();
private boolean mScanning;

private Handler mHandler;

ArrayList<String> mylist = new ArrayList<String>();
private static final int REQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 50000;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 456;

UsersAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//getActionBar().setTitle("abc");

mHandler = new Handler();

requestPermissions(new String{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
mBluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null ) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
if( !mBluetoothAdapter.isEnabled())
{
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, 1);
}
if( !mBluetoothAdapter.isDiscovering()) {
Intent discoverableIntent =
new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
// Construct the data source

ArrayList<ViewHolder> arrayOfUsers = new ArrayList<ViewHolder>();

// Create the adapter to convert the array to views

adapter = new UsersAdapter(this, arrayOfUsers);


ListView listView = (ListView) findViewById(R.id.mobile_list);
listView.setAdapter(adapter);
ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
adapter.add(newUser2);

}


private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
adapter.add(newUser2);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);


if(arrayOfUsers2.isEmpty())
{
ViewHolder currentX=new ViewHolder("No devices ","found");
adapter.add(currentX);
}
else {
Iterator<ViewHolder> it = arrayOfUsers2.iterator();
while (it.hasNext()) {
ViewHolder currentX = it.next();
adapter.add(currentX);
}
}
// Do something with the value

}
}, SCAN_PERIOD);

mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
//invalidateOptionsMenu();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions, @NonNull int grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, yay! Start the Bluetooth device scan.
scanLeDevice(true);
} else {
// Alert the user that this application requires the location permission to perform the 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() {
//mLeDeviceListAdapter.addDevice(device);
//mLeDeviceListAdapter.notifyDataSetChanged();

//ViewHolder newUser = new ViewHolder("Nathan", "San Diego");
String deviceName=null, deviceAddress=null;
if(device!=null)
deviceName= device.getName();
if (!(deviceName != null && deviceName.length() > 0))
deviceName = "unknown device";
if(device!=null)
deviceAddress= device.getAddress();
ViewHolder newUser = new ViewHolder(deviceName, deviceAddress);
ViewHolder newUser2 = new ViewHolder("adtv","vvg");
//if(!arrayOfUsers2.contains(newUser))
arrayOfUsers2.add(newUser);
adapter.add(newUser);
}
});
}
};
public class UsersAdapter extends ArrayAdapter<ViewHolder> {


public UsersAdapter(Context context, ArrayList<ViewHolder> users) {

super(context, 0, users);

}



@Override

public View getView(int position, View convertView, ViewGroup parent) {

// Get the data item for this position

ViewHolder user = getItem(position);


// Check if an existing view is being reused, otherwise inflate the view

if (convertView == null) {

convertView = LayoutInflater.from(getContext()).inflate(R.layout.bt_details, parent, false);

}

// Lookup view for data population

TextView tvName = (TextView) convertView.findViewById(R.id.DeviceName);

TextView tvHome = (TextView) convertView.findViewById(R.id.DeviceAddress);

// Populate the data into the template view using the data object

tvName.setText(user.deviceName);

tvHome.setText(user.deviceAddress);

// Return the completed view to render on screen

return convertView;

}

}
public class ViewHolder {
String deviceName;
String deviceAddress;

public ViewHolder(String device, String __address) {
this.deviceName =device;
this.deviceAddress= __address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ViewHolder a = (ViewHolder) o;
return Objects.equals(deviceAddress, a.deviceAddress);
}
}



}


}});
ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
adapter.add(newUser2);

}


private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
adapter.add(newUser2);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);


if(arrayOfUsers2.isEmpty())
{
ViewHolder currentX=new ViewHolder("No devices ","found");
adapter.add(currentX);
}
else {
Iterator<ViewHolder> it = arrayOfUsers2.iterator();
while (it.hasNext()) {
ViewHolder currentX = it.next();
adapter.add(currentX);
}
}
// Do something with the value

}
}, SCAN_PERIOD);

mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}

}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions, @NonNull int grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, yay! Start the Bluetooth device scan.
scanLeDevice(true);
} else {
// Alert the user that this application requires the location permission to perform the 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() {

String deviceName=null, deviceAddress=null;
if(device!=null)
deviceName= device.getName();
if (!(deviceName != null && deviceName.length() > 0))
deviceName = "unknown device";
if(device!=null)
deviceAddress= device.getAddress();
ViewHolder newUser = new ViewHolder(deviceName, deviceAddress);
ViewHolder newUser2 = new ViewHolder("adtv","vvg");
//if(!arrayOfUsers2.contains(newUser))
arrayOfUsers2.add(newUser);
adapter.add(newUser);
}
});
}
};
public class UsersAdapter extends ArrayAdapter<ViewHolder> {


public UsersAdapter(Context context, ArrayList<ViewHolder> users) {

super(context, 0, users);

}



@Override

public View getView(int position, View convertView, ViewGroup parent) {

// Get the data item for this position

ViewHolder user = getItem(position);


// Check if an existing view is being reused, otherwise inflate the view

if (convertView == null) {

convertView = LayoutInflater.from(getContext()).inflate(R.layout.bt_details, parent, false);

}

// Lookup view for data population

TextView tvName = (TextView) convertView.findViewById(R.id.DeviceName);

TextView tvHome = (TextView) convertView.findViewById(R.id.DeviceAddress);

// Populate the data into the template view using the data object

tvName.setText(user.deviceName);

tvHome.setText(user.deviceAddress);

// Return the completed view to render on screen

return convertView;

}

}
public class ViewHolder {
String deviceName;
String deviceAddress;

public ViewHolder(String device, String __address) {
this.deviceName =device;
this.deviceAddress= __address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ViewHolder a = (ViewHolder) o;
return Objects.equals(deviceAddress, a.deviceAddress);
}
}



}



I am trying to scan for nearby bluetooth devices using BLE API.I had tested with this code,it was working till today morning.But I got 2 new devices and am putting this code on both of them and testing. They are not discovering each other.Also it is not discovering my laptop. Earlier I could discover other devices nearby. Hope they are not nearby now.Both of them are discovering same one device but not discovering each other.
build.gradle(ModuleApp)


apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.root.securityalert"
minSdkVersion 23
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.0'
implementation 'com.android.support:design:27.0.2'-this line is in red(error)
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}



Build.gradle(Project:xyz)


// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.2.50'

repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}



logcat error


build failed 213ms
Run build 90ms
Load build 3ms
Configure build 78ms
Load projects 6ms
rootProject
Configure project : 61ms
Apply plugin org.gradle.help-tasks 1ms
Apply script build.gradle 53ms--- this line is in red(error)



Also I get this error


Could not find method compile() for arguments [io.reactivex.rxjava2:rxjava:2.0.0] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.




1 Answer
1



Make sure you have the API levels ironed out because most of the Android BLE constructs are > API 21. The one where adapter.startLeScan{} is for lower API devices and that maybe the reason why new devices are not being able to detect the other devices. I created a simple BLE scanner with Rx using Kotlin. This makes it easier to handle and schedule on to threads.


adapter.startLeScan{}



You can configure it using the Builder and setting stopCallback with a condition would stop the scan upon hitting that condition.


Builder


stopCallback


import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.os.Build
import android.support.annotation.RequiresApi
import io.reactivex.*
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.jetbrains.anko.bluetoothManager

class BleScannerFlowable private constructor(
private val adapter: BluetoothAdapter,
private val stopCallback: ((BluetoothDevice) -> Boolean)? = null,
private val scanFilter: Array<out ScanFilter>? = null,
private val scanSettings: ScanSettings? = null
): FlowableOnSubscribe<BluetoothDevice> {

override fun subscribe(e: FlowableEmitter<BluetoothDevice>) {
if (isAboveLollipop()) {
setupSubscriber21(e)
} else {
setupSubscriber(e)
}
}

private fun setupSubscriber(emitter: FlowableEmitter<BluetoothDevice>) {
adapter.startLeScan { device, rssi, scanRecord ->
if (stopCallback?.invoke(device) == true) {
adapter.stopLeScan { device, rssi, scanRecord ->
emitter.onComplete()
}
}
emitter.onNext(device)
}
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun setupSubscriber21(emitter: FlowableEmitter<BluetoothDevice>) {
val isFilterSet = scanFilter?.isNotEmpty() == true
val filters = if (isFilterSet) scanFilter!!.toMutableList() else mutableListOf()
adapter.bluetoothLeScanner?.startScan(filters, scanSettings, ScanCaller(emitter))
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private inner class ScanCaller(private val emitter: FlowableEmitter<BluetoothDevice>) : ScanCallback() {
override fun onScanFailed(errorCode: Int) {
emitter.onError(Throwable("Scan failed with code $errorCode"))
}

override fun onScanResult(callbackType: Int, result: ScanResult?) {
if (result != null && result.device != null) {
if (stopCallback?.invoke(result.device) == true) {
adapter.bluetoothLeScanner?.stopScan(this)
emitter.onComplete()
} else {
emitter.onNext(result.device)
}
}
}
}

class Builder(private val adapter: BluetoothAdapter) {
private var scanFilter: Array<out ScanFilter>? = null
private var stopCallback: ((BluetoothDevice) -> Boolean)? = null
private var settingsBuilder: ScanSettings.Builder? = null
private var scanSettings: ScanSettings? = null

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setFilterByName(name: String) : Builder {
setScanFilters(ScanFilter.Builder()
.setDeviceName(name)
.build())
return this
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setScanFilters(vararg scanFilter: ScanFilter) : Builder{
this.scanFilter = scanFilter
return this
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setScanMode(scanMode: ScanModes) : Builder{
getSettingsBuilder().setScanMode(scanMode.settingId)
return this
}

fun setStopCallback(function: (BluetoothDevice) -> Boolean) : Builder {
this.stopCallback = function
return this
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setScanSettings(scanSettings: ScanSettings) : Builder {
this.scanSettings = scanSettings
return this
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun build(): Flowable<BluetoothDevice> =
Flowable.create(BleScannerFlowable(adapter,
stopCallback,
scanFilter,
scanSettings ?: settingsBuilder?.build()),
BackpressureStrategy.LATEST)

fun buildLower(): Flowable<BluetoothDevice> =
Flowable.create(BleScannerFlowable(adapter, stopCallback),
BackpressureStrategy.LATEST)

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun getSettingsBuilder() : ScanSettings.Builder {
if (settingsBuilder == null) {
settingsBuilder = ScanSettings.Builder()
}
return settingsBuilder!!
}
}

}


@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
enum class ScanModes(val settingId: Int) {
LOW_LATENCY(ScanSettings.SCAN_MODE_LOW_LATENCY),
LOW_POWER(ScanSettings.SCAN_MODE_LOW_POWER),
BALANCED(ScanSettings.SCAN_MODE_BALANCED),
OPPORTUNISTIC(ScanSettings.SCAN_MODE_BALANCED)
}

fun startScanning() {
val adapter = applicationContext.bluetoothManager.adapter ?: return
val bleScanner = if (isAboveLollipop()) {
BleScannerFlowable.Builder(adapter)
.setScanMode(ScanModes.BALANCED)
.setStopCallback { it.name == "MyBleDevice" }
.build()
} else {
BleScannerFlowable.Builder(adapter)
.setStopCallback { it.name == "MyBleDevice" }
.buildLower()
}

bleScanner
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// do something with the bluetooth device
}, {
// handle error
it.printStackTrace()
}, {
// do on finish scan
})
}

fun isAboveLollipop() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP





what language is this,does not look like java? I read java 5 years back.Now it may have changed. Is this BLE compatible?
– mithun
Jun 28 at 7:23





This is Kotlin as mentioned in the comment. Yes it is inverse compatible with Java and is meant for BLE scanning
– HawkPriest
Jun 28 at 7:32


Kotlin





I created a class RX_BLE.kt and copied the whole content, but it is showing errors on some imports.
– mithun
Jun 28 at 7:33





can you give me a link where I can study about kotlin in short
– mithun
Jun 28 at 7:36





You need to configure kotlin in project. If you have the latest android studio, you have to go to tools->kotlin->configure kotlin in project and select the project or module for kotlin support. You can find basic introduction to android kotlin here: developer.android.com/kotlin. Better tutorial is with a simple example is: android.jlelse.eu/…
– HawkPriest
Jun 28 at 7:45


tools->kotlin->configure kotlin in project






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

how to run turtle graphics in Colaboratory

Export result set on Dbeaver to CSV