I've been having some problems getting Bluetooth to scan for devices with my Samsung Galaxy s5.
I'm on Android 6.0 and have set up permissions for my app to scan like so:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
// Android M Permission check
if (this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can devices.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
I assume this is working correctly because I got the pop-up asking for permissions which I accepted.
My scan function:
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;
Log.i("start" , "Stopping scan...");
mBluetoothLeScanner.stopScan(mScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
Log.i("start" , "Starting scan...");
mBluetoothLeScanner.startScan(mScanCallback);
} else {
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
}
}
Now it stops and starts scanning correctly since Logcat is giving me logs. But it's just not finding any devices which is really weird because I'm sitting next to my laptop and a second phone both with Bluetooth enabled.
Here's my callback by the way, if anyone is interested:
Private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
System.out.println("BLE// onScanResult");
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
System.out.println("BLE// onBatchScanResults");
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
System.out.println("BLE// onScanFailed");
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
Now as you can see the scan is not failing since Logcat is not giving me a scan failed log, but apperantly its also not finding any devices...
Logcat:
06-07 17:13:02.622 16802-16802/com.example.joey.findmycar I/start: Starting scan...
06-07 17:13:02.802 16802-16802/com.example.joey.findmycar W/DisplayListCanvas: DisplayListCanvas is started on unbinded RenderNode (without mOwningView)
06-07 17:13:02.882 16802-16802/com.example.joey.findmycar I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy#c8fddba time:699666571
06-07 17:13:14.632 16802-16802/com.example.joey.findmycar I/start: Stopping scan...
I've added the correct permissions to my Manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
I've tried almost every possibility and am starting to think the Bluetooth on my phone might be broken somehow, which is weird because I can manually detect and connect to devices in the Android settings.
Your code should work fine if you are scanning for BLE devices like a heart rate monitor or a proximity sensor. The problem could be that with this code your app is the GATT client and is searching for a GATT server.
So if you want to connect to another phone you could write a GATT server app and run it on the other phone (as indicated in the last paragraph here)
I found this in Android 6.0 APIs updates:
Improved Bluetooth Low Energy Scanning
If your app performs performs Bluetooth Low Energy scans, use the new setCallbackType() method to specify that you want the system to notify callbacks when it first finds, or sees after a long time, an advertisement packet matching the set ScanFilter. This approach to scanning is more power-efficient than what’s provided in the previous platform version.
There is complete doc by google u go through it:
https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
Related
Im developping app for tablets, its a car navigation.
What I need to do is - if the driver turns on car the tablet turns on screen, if the car is shut down display is off.
My first thought is to keep tablet plugged to the car charger and if the charging is detected turn on display. If the charging is not detected turn off display.
App would run in KIOSK mode. What would be the best approach to detect charging status while screen is off ?
It's pretty simple.
First you need to listen for charging state changes. It can be done by:
//Create receiver
BroadcastReceiver powerChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(Intent.ACTION_POWER_CONNECTED.equals(action)){
turnScreenOn();
} else if(Intent.ACTION_POWER_DISCONNECTED.equals(action)){
turnScreenOff();
}
}
};
//Register receiver
IntentFilter i = new IntentFilter();
i.addAction(Intent.ACTION_POWER_CONNECTED);
i.addAction(Intent.ACTION_POWER_DISCONNECTED);
registerReceiver(powerChangeReceiver, i);
and now all you need to do is to implement turnScreenOn() and turnScreenOff() methods:
public void turnScreenOn(){
runOnUiThread(() -> {
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
#SuppressWarnings("deprecation")
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP, "example:tag");
wl.acquire();
});
}
public void turnScreenOff(){
if(policyManager.isDeviceOwnerApp(getPackageName())) {
runOnUiThread(() -> {
policyManager.lockNow();
});
}
}
and don't forget to add WAKE_LOCK permission to your Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Hope it helps!
We have created an Android app which scans for specific manufacturer devices by using BLE (Bluetooth Low Energy). Actually everything is working fine, but we have one problem. Sometimes after a few scans the tablet can no longer find any devices in all scans that follow. The only one workaround is to turn Bluetooth off and on again, then the tablet can find devices again. The environment has about 30 BLE devices and 20 of them are the devices we would like to find and filter. The client can reproduce it but we unfortunately can not so it is hard to debug.
The scan should only work in foreground, there is not need to scan in background. The user can restart the scan just after the scan ends. I already know the 30 seconds / 5 scans limit, this is handled fine.
The target tablet is running Android 8.1, the project settings are:
compileSdkVersion = 28
buildToolsVersion = '30.0.2'
minSdkVersion = 17
targetSdkVersion = 28
BLE Scan Settings:
ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
.setReportDelay(0L)
.build();
new ScanFilter.Builder().setManufacturerData(MANUFACTURE_ID, new byte[0]).build());
When the Scan is working, Logs looks like this:
I/BleScanService: Start Scanning
D/Fragment: refreshUIElements true
D/BluetoothAdapter: isLeEnabled(): ON
D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=7 mScannerId=0
I/Fragment$ScannerBroadcastReceiver: listing device BLE: deviceMacAddress:EC:A5:80:12:D4:1A, nwpBleMacAddress:XXXXXXXXXXEE, name:Device
I/Fragment$ScannerBroadcastReceiver: listing device BLE: deviceMacAddress:CE:A8:80:60:C9:A8, nwpBleMacAddress:XXXXXXXXXX08, name:Device
D/BluetoothAdapter: isLeEnabled(): ON
I/BleScanService: Stopped Scanning
When the scan stops working, i get logs like this (interesting is that the isLeEnabled(): ON log entry no longer appears):
07-28 14:02:48:310 I/BleScanService(2) : Start Scanning
07-28 14:02:48:316 I/BleScanService(2) : Resume Scanning in 0 ms
07-28 14:02:48:324 D/Fragment(2) : refreshUIElements true
07-28 14:02:54:357 I/BleScanService(2) : Stopped Scanning
This is the ScanService class which does all the magic:
public class BleScanService {
private final Handler scanHandler;
private final Context context;
private final Settings settings;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;
private final Set<String> discoveredDevices;
private final List<Long> scanTimestamps = new ArrayList<>(10);
private boolean scanning;
#Inject
public BleScanService(Context context, Settings settings) {
this.context = context;
this.settings = settings;
this.scanHandler = new Handler();
}
public BluetoothDevice getBluetoothDevice(String deviceMacAddress) {
return bluetoothAdapter.getRemoteDevice(deviceMacAddress);
}
public boolean isScanning() {
return scanning;
}
public void startScanning() {
if (scanning) {
Timber.i("Ignore start scanning request because scanning is already active");
return;
}
try {
scanning = true;
discoveredDevices.clear();
initService();
long nextScannerAvailability = getNextScannerAvailability();
Timber.i("Resume Scanning in %s ms", nextScannerAvailability);
scanHandler.postDelayed(this::scan, nextScannerAvailability);
} catch (Exception e) {
Timber.e(e);
stopScanning();
}
}
private void scan() {
bluetoothLeScanner.startScan(BleScanSettings.getManufacturerScanFilter(), BleScanSettings.getScanSettings(), leScanCallback);
scanHandler.postDelayed(() -> {
stopScanning();
Timber.i("Devices shown %s", String.join(", ", discoveredDevices));
}, 8000);
}
private void initService() {
if (bluetoothAdapter == null) {
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
}
if (bluetoothLeScanner == null) {
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
}
}
}
private long getNextScannerAvailability() {
final long currentTimeMillis = System.currentTimeMillis();
scanTimestamps.removeIf(t -> currentTimeMillis - t > 30000);
if (scanTimestamps.size() < 4) {
return 0;
}
long oldestActiveTimestamp = scanTimestamps.get(scanTimestamps.size() - 4);
return 30000 - (currentTimeMillis - oldestActiveTimestamp) + 500;
}
public void stopScanning() {
if (bluetoothAdapter == null) {
return;
}
try {
bluetoothLeScanner.stopScan(leScanCallback);
} catch (Exception e) {
Timber.w(e, "Exception occurred while attempting to stop BLE scanning.");
}
if (scanning) {
scanTimestamps.add(System.currentTimeMillis());
}
scanHandler.removeCallbacksAndMessages(null);
scanning = false;
Timber.i("Stopped Scanning");
}
protected void broadcastDetectedDevice(Intent intent) {
context.sendBroadcast(intent);
}
private final ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning) {
Timber.i("ignore scanning result because the scanning is paused");
return;
}
if (result.getScanRecord() == null) {
Timber.i("Skip unsupported device %s", result.getDevice().getName());
return;
}
BluetoothDevice device = result.getDevice();
String deviceMacAddress = device.getAddress();
if (isAlreadyDiscovered(deviceMacAddress)) {
return;
}
byte[] manufacturerSpecificData = result.getScanRecord().getManufacturerSpecificData(BleScanSettings.MANUFACTURE_ID);
final Optional<Intent> msdIntent = BleDetectedDeviceIntentHelper
.getIntentFromManufacturerSpecificData(deviceMacAddress, manufacturerSpecificData);
if (msdIntent.isPresent()) {
handleManufacturerDataScan(deviceMacAddress, msdIntent.get());
}
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Timber.i("SCAN FAILED");
stopScanning();
}
Permissions which are used and also requested:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<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-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.CAMERA" />
Any ideas? Thank you for your help!
The behavior you describe happens when an app scans too frequently i.e. more than 5 times per 30 seconds. It seems you are scanning too frequently for some reason...
Stopping and re-starting scan automatically after the 8 secs delay may solve the problem.
Also, from Android 12+, apps require BLUETOOTH_SCAN permission in order to scan for Bluetooth devices.
Add this line to your app manifest:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
Here is a sample code to request the permission:
public void requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (this.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
Manifest.permission.BLUETOOTH_SCAN)) {
// display permission rationale in snackbar or dialog message
} else {
this.requestPermissions(new String[]{
Manifest.permission.BLUETOOTH_SCAN
}, MyActivity.REQUEST_BLUETOOTH_PERMISSIONS);
}
}
}
}
I am a complete novice in Java and Android. I am trying to create a test app to listen for BLE and BT devices nearby. I have another device where I wrote some logic to broadcast its BLE beacons. I verified it using a playstore app. Now I am trying to write my own app on Android.
I have been reading the Android developer pages for guidance. I have literally followed every step of the following pages
https://developer.android.com/guide/topics/connectivity/bluetooth/setup
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
Also, Note that I have used BARE MINIMUM CODE from the Android Developers page So here is what I have done.
1. First off I have added my permissions under AndroidManifest
Note1 : I am deploying this app to My phone running Android 11
Note2 : All this code is written inside MainActivity. I have not created any other activity class
<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_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2. Next I check if my BT is enabled.
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
I get the success message that BT is of course enabled
3. Next I check if my device supports BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
blefinder.append("\nBLE NOT SUPPORTED ON THIS DEVICE : ");
finish();
}
else{
blefinder.append("\nBLE IS SUPPORTED ON THIS DEVICE : ");
}
I get the message that BLE is supported
4. Next I list my already paired/bonded devices
For this I call ListPairedAndBondedDevices(); in onCreate() itself right after the above steps. Function Definition Below.
private void ListPairedAndBondedDevices(){
#SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
blefinder.append("\nPAIRED/BONDED DEVICES");
for (BluetoothDevice device : pairedDevices) {
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
}
This also works like a charm and prints out my paired devices. The next 2 parts is where I face the problem.
5. The Problem Step | Part 1:
Here I register a Broadcast receiver to discover all BT devices in the vicinity. I've unbonded my BT headphones and kept it in pairing mode to verify this.
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
Broadcast Receiver implementation
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
#SuppressLint("MissingPermission")
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);
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
};
So This part didn't Work :(
If you see above, I am registering the BTReceiver in onCreate right after listing the already paired devices (by calling ListPairedAndBondedDevices()).
When I ran the debugger, this broadcast receiver never gets called.
6. The Problem Step | Part 2:
Right after this I try to scan for BLE Devices as well by callin scanLeDevice()
ListPairedAndBondedDevices(); // From previous snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // From previous snippet
registerReceiver(BTReceiver, filter);// From previous snippet
scanLeDevice(); // ---------------->>> CALLING THIS FUNCTION TO SCAN FOR BLE DEVICES
Implementation of scanLeDevice()
private void scanLeDevice() {
if (!scanning) {
// Stops scanning after a predefined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN... TIMEOUT REACHED");
}
}, SCAN_PERIOD);
scanning = true;
bluetoothLeScanner.startScan(leScanCallback);
} else {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN");
}
}
Unfortunately this also fails. The debugger tells me that this part of the code is getting called.
And after 30 seconds of SCAN_PERIOD (The TIMEOUT that I've set), I get the message that the scanning has stopped (STOPPING BLE SCAN)
Now I have implemented the leScanCallback as well (i.e the Device Scan Callback)
private ScanCallback leScanCallback =
new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
blefinder.append("SOMETHING GOT SCANNED?");
blefinder.append("\n"+result.getDevice().toString());
// leDeviceListAdapter.addDevice(result.getDevice());
// leDeviceListAdapter.notifyDataSetChanged();
}
};
Notice that I am not using a ListAdapter since I have no idea about that concept. Hence for starters I am just trying to dump the results in a TextView represented by blefinder . This blefinder prints all the other texts so there is nothing wrong with that TextView variable. When I ran using the, debugger, it is not entering into the leScanCallback piece of code definition at all, even after 30 seconds, after scanLeDevice() function is executed.
I am a little lost here. Is there something I may be missing or doing wrong. It is supposed to be a simple, list the ble/bt devices around my vicinity.
I am happy to share any further information if I have missed. Just let me know in the comments.
Assuming you've done with the permissions that I've mentioned in the comments, we can implement a clean bluetooth LE scanner object and then use it in the UI.
First we implement a result consumer interface in order to deliver the results to the consumers which call the BleScanner.scan() method.
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
Now we need to implement the scanner object that manages the scanning events:
public class BleScanner {
private static final String TAG = BleScanner.class.getSimpleName();
private BluetoothLeScanner leScanner = null;
private BluetoothAdapter bleAdapter = null;
private Handler uiHandler = new Handler(Looper.getMainLooper);
private ScanResultConsumer scanResultConsumer;
private boolean scanning = false;
private final ArrayList<BluetoothDevice> foundDeviceList = new ArrayList<>();
public BleScanner(Context context) {
final BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bluetoothManager.getAdapter();
if(bleAdapter == null) {
Log.d(TAG, "No bluetooth hardware.");
}
else if(!bleAdapter.isEnabled()){
Log.d(TAG, "Blutooth is off.");
}
}
public void scan(ScanResultConsumer scanResultConsumer, long scanTime){
foundDeviceList.clear();
if (scanning){
Log.d(TAG, "Already scanning.");
return;
}
Log.d(TAG, "Scanning...");
if(leScanner == null){
leScanner = bleAdapter.getBluetoothLeScanner();
}
if(scanTimeMs > 0) {
uiHandler.postDelayed(()-> {
if (scanning) {
Log.d(TAG, "Scanning is stopping.");
if(leScanner != null)
leScanner.stopScan(scanCallBack);
else
Log.d(TAG,"Scanner null");
setScanning(false);
}
}, scanTimeMs);
}
this.scanResultConsumer = scanResultConsumer;
leScanner.startScan(scanCallBack);
setScanning(true);
}
private final ScanCallback scanCallBack = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning){
return;
}
if(foundDeviceList.contains(result.getDevice())) {
// This device has already been found
return;
}
// New device found, add it to the list in order to prevent duplications
foundDeviceList.add(result.getDevice());
if(scanResultConsumer != null) {
uiHandler.post(() -> {
scanResultConsumer.onDeviceFound(result.getDevice(),
result.getScanRecord().getBytes(), result.getRssi());
});
}
}
};
public boolean isScanning(){
return scanning;
}
void setScanning(boolean scanning){
this.scanning = scanning;
uiHandler.post(() -> {
if(scanResultConsumer == null) return;
if(!scanning){
scanResultConsumer.onScanningStopped();
// Nullify the consumer in order to prevent UI crashes
scanResultConsumer = null;
} else{
scanResultConsumer.onScanningStarted();
}
});
}
}
Finally we can use this clean implementation in anywhere we need. But do note that a context must be provided in order to create a BleScanner object.
public class MainActivity extends AppCompatActivity {
private BleScanner bleScanner;
private Button buttonScan
// Other codes...
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other codes...
bleScanner = new BleScanner(getApplicationContext());
// Other codes...
// For example if you want to start scanning on a button press
// Let's say you have a button called buttonScan and initiated it
buttonScan = findViewById(R.id.scan_button);
buttonScan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bleScanner.scan(new ScanResultConsumer {
#Override
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi) {
// TODO Here you have a newly found device, do something.
}
#Override
q public void onScanningStarted() {
// TODO Scanning has just started, you may want to make some UI changes.
}
#Override
public void onScanningStopped() {
// TODO Scanning has just stopped, you may want to make some UI changes.
}
});
}
});
}
}
Note: I written this code in a plain editor not in Android Studio. So there may be some errors, let me know if any.
First you should check if your app was granted the location permission(s) in the Settings app > Apps <your_app> > permissions. Some permissions (like ACCESS_*_LOCATION and BLUETOOTH_ADMIN) need to be requested at runtime and granted by the user through a popup. Normally you should get a SecurityException or a logcat warning when trying to execute code requiring permissions which your app doesn't have, but it's not uncommon for android to skip over error handling.
Consider using this method to start the scan in order check its result code for potential additional info about what is (not) going on.
You might also get some clues by logging all actions received in BTReceiver.onReceive(), not just action found.
Lastly check if the location settings on your device to ensure that bluetooth scanning is turned on (Settings app > location > wifi and bluetooth scanning )
I'm developing and Android app with Bluetooth but sometimes I have problems with LE devices discovering: usually the discovery callback return me the the devices found but sometimes, stops working and does not return me the devices.
I tested the code (in debug mode) in different devices (Samsung, LG) and with different Android version (8.0, 6.0, 4.4) but the problem is the same, is not systematic and after some time, it return to works fine.
I have applied all the suggestions found in internet:
Android can't find any BLE devices
Bluetooth LE Scanning Sometimes Doesn't Find Devices
Android Bluetooth Low Energy code compatible with API>=21 AND API<21
but the problem persist.
Below a snips of 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" />
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
.
.
.
and bluetooth code:
public BLEH_RES StartDiscovery()
{
.
.
.
CheckPermission();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
return(scanLeDevice21(true));
else
return(scanLeDevice18(true));
}
/**
* Scan BLE devices on Android API 20 to last version (Android 9.0)
*
* #param enable Enable scan
*/
#RequiresApi(21)
private BLEH_RES scanLeDevice21(boolean enable)
{
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null)
return(BLEH_RES.BLE_NOT_SUPPORTED);
if (enable)
{
ScanSettings.Builder scanSettings = new ScanSettings.Builder();
scanSettings.setScanMode(SCAN_MODE_LOW_LATENCY);
bluetoothLeScanner.startScan(null, scanSettings.build(), BLEScanCallback);
}
else
bluetoothLeScanner.stopScan(BLEScanCallback);
return(BLEH_RES.OK);
}
/**
* Scan BLE devices on Android API 18 to 20
*
* #param enable Enable scan
*/
private BLEH_RES scanLeDevice18(boolean enable)
{
if(bluetoothAdapter == null)
return(BLEH_RES.BT_NOT_SUPPORTED);
if (enable)
{
bluetoothAdapter.startDiscovery();
bluetoothAdapter.startLeScan(mLeScanCallback);
}
else
{
bluetoothAdapter.cancelDiscovery();
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
return(BLEH_RES.OK);
}
#RequiresApi(21)
private ScanCallback BLEScanCallback = new ScanCallback()
{
#Override
public void onScanResult(int callbackType, ScanResult result)
{
addDevice(result.getDevice(), result.getRssi(), result.getScanRecord().getBytes());
}
#Override
public void onScanFailed(int errorCode)
{
for(ErrorEvent ee:errorEventList)
ee.onError(BLEH_EVENT_ERROR.SCAN_FAILED);
}
};
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback()
{
#Override
public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi,byte[] scanRecord)
{
addDevice(bluetoothDevice, rssi, scanRecord);
}
};
Can you suggest me anything to solve this problem?
Make sure that GPS is enabled all the time you searching devices. Also suggest you to watch this about BLE
https://youtu.be/jDykHjn-4Ng
I solved my problem by following the suggestions in this link: https://stackoverflow.com/a/42267678/7006955
I add to my code all the code suggested and it works fine also in Android 4
I tried to create a audioReceiver Broadcast. To allow my user to ONLY listen to music if the HeadSet in plug-in.
Here is the code :
private BroadcastReceiver audioReceiver = new BroadcastReceiver()
{ #Override
public void onReceive(Context context, Intent intent)
{ AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
headsetIsPluggedIn = (intent.getExtras().getInt("state")==1);
if(headsetIsPluggedIn) //plugged
{ Log.d("", "BroadcastReceiver - Unmute sound");
audio.setStreamMute(AudioManager.STREAM_MUSIC, false);
}
else //unplugged
{ Log.d("", "BroadcastReceiver - Mute sound");
audio.setStreamMute(AudioManager.STREAM_MUSIC, true);
// Inform user
Toast.makeText(DuplicatedPlayerActivity.this, "Please plug in your headset to enjoy the sound.", Toast.LENGTH_SHORT).show();
}
}
};
#Override
public void onResume() {
super.onResume();
if (HEADSET_ONLY) { registerReceiver(audioReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); }
Manifest
<receiver android:name="com.juno.brheadset.HeadsetStateReceiver">
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG"/>
</intent-filter>
</receiver>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
OK, Here is the fun part. If you restart the device, run my app. This function will not work.
If you insert the headset jack in the phone at lest once! quit my app, restart the app. The function will work fine, until the user restart the phone again.
Now, why does this function only works if the user insert the jack in the phone?
"Now, Why does this function only works if the user insert the jack in the phone ?" --> Because you have written your code inside the receiver of action "android.intent.action.HEADSET_PLUG". Your code will only execute when you receive the headset_plug broadcast.