I’m creating an Android app that scans all nearby BLE (Bluetooth Low Energy) devices using Android Studio.
I think I have implemented all these documentations:
- https://developer.android.com/guide/topics/connectivity/bluetooth/ble-overview
- https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java
- https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java
- https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
But as you can see in the log below, (the problem) there are no BLE devices scanned by the app.
Here are the logs in the logcat:
I think it could happen because the BLE scan callback
is not called (no BLE scan callback in the log).
So my question is what is the solution for my code? Did I miss coding something?
Here is my code
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example"> <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.bluetooth_le" android:required="true" /> <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/Theme.Example"> <activity android:name=".ui.authentication.signin.SignInActivity" /> <activity android:name=".ui.authentication.signup.SignUpActivity" /> <activity android:name=".ui.home.HomeActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
BluetoothHelper.java:
package com.example.util; import android.Manifest; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Handler; import android.util.Log; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; public class BluetoothHelper { static int REQUEST_ENABLE_BT = 1; static int PERMISSION_CODE = 1; static String TAG = "Bluetooth"; static boolean isScanning = false; // SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java // CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10 public static void android10BluetoothPermission (Context context, Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, PERMISSION_CODE ); } } } // SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java // GET BLUETOOTH ADAPTER public static void setupBluetooth (BluetoothAdapter bluetoothAdapter) { if (bluetoothAdapter == null) { Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null"); } else { Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null"); } } // ENABLE BLUETOOTH public static void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) { Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled()); if (!bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); Log.d(TAG, "Bluetooth: " + "bluetooth is enabled"); } else { Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled"); } } // SOURCE FOR FIND BLUETOOTH DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices // QUERY PAIRED DEVICES public static Set<BluetoothDevice> queryPairedDevices (BluetoothAdapter bluetoothAdapter) { Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices(); Log.d(TAG, "PairedDevices: " + pairedDevices.toString()); return pairedDevices; } // GET ALL NAMES AND MAC ADDRESSES public static ArrayList<HashMap<String, String>> getNamesAndMacAddresses(Set<BluetoothDevice> bluetoothDevices) { ArrayList<HashMap<String, String>> arrayList = new ArrayList<>(); if (bluetoothDevices.size() > 0) { for (BluetoothDevice device : bluetoothDevices) { String deviceName = device.getName(); String deviceMacAddress = device.getAddress(); HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("deviceName", deviceName); hashMap.put("deviceMacAddress", deviceMacAddress); arrayList.add(hashMap); } } Log.d(TAG, "NamesAndMacAddress: " + arrayList); return arrayList; } // SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java // SCAN BLE DEVICES public static void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) { int SCAN_DURATION = 10000; if(!isScanning) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { isScanning = false; bluetoothLeScanner.stopScan(bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "stop scanning"); } }, SCAN_DURATION); isScanning = true; bluetoothLeScanner.startScan(bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "start scanning"); } else { isScanning = false; bluetoothLeScanner.stopScan(bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "stop scanning"); } } // BLE SCAN CALLBACK private static ScanCallback bleScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); Log.d(TAG, "ScanCallback: " + result.getDevice()); } }; }
HomeActivity.java:
package com.example.ui.home; import androidx.appcompat.app.AppCompatActivity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.BluetoothLeScanner; import android.os.Bundle; import android.view.View; import com.example.R; import com.example.databinding.ActivityHomeBinding; import com.example.util.AuthenticationHelper; import com.example.util.BluetoothHelper; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; public class HomeActivity extends AppCompatActivity implements View.OnClickListener { ActivityHomeBinding binding; Set<BluetoothDevice> pairedDevices; ArrayList<HashMap<String, String>> arrayList; BluetoothLeScanner bluetoothLeScanner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityHomeBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setUpBluetooth(); binding.buttonKeluar.setOnClickListener(this); } @Override protected void onStart() { super.onStart(); AuthenticationHelper.checkHasTheUserLoggedIn(this); } @Override public void onClick(View v) { if(v.getId() == R.id.buttonKeluar) { AuthenticationHelper.signOutUser(this); } } private void setUpBluetooth() { BluetoothHelper.android10BluetoothPermission(this, HomeActivity.this); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); BluetoothHelper.setupBluetooth(bluetoothAdapter); BluetoothHelper.enableBluetooth(this, bluetoothAdapter); pairedDevices = BluetoothHelper.queryPairedDevices(bluetoothAdapter); arrayList = BluetoothHelper.getNamesAndMacAddresses(pairedDevices); bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); BluetoothHelper.scanBLEDevices(bluetoothLeScanner); } }
UPDATE
I added this code into android10BluetoothPermission()
function in BluetoothHelper.java
class but still didn’t work.
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_CODE ); } if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( activity, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_CODE ); }
Advertisement
Answer
Finally, I have modified my code so the code now is working. Here it is:
ScanAndPairActivity.java (I’m creating a new activity for scanning and pairing BLE devices):
package com.example.ui.scanandpair; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.Toast; import com.example.R; import com.example.databinding.ActivityScanAndPairBinding; import com.example.entity.BLEDeviceEntity; import java.util.ArrayList; import java.util.List; public class ScanAndPairActivity extends AppCompatActivity { // GENERAL String TAG = getClass().getSimpleName(); ActivityScanAndPairBinding binding; // BLUETOOTH int REQUEST_ENABLE_BT = 1; boolean isScanning = false; BluetoothAdapter bluetoothAdapter; BluetoothLeScanner bluetoothLeScanner; ScanAndPairAdapter scanAndPairAdapter; List<BLEDeviceEntity> bleDeviceEntityList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityScanAndPairBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); android10BluetoothPermission(); scanAndPairAdapter = new ScanAndPairAdapter(this); bleDeviceEntityList = new ArrayList<>(); } // SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java // CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10 private void android10BluetoothPermission () { boolean isLocationPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; boolean isBGLocationAccessNotGranted = false; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { isBGLocationAccessNotGranted = ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED; } boolean isLocationAccessNotGranted = ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED; if (isLocationPermissionRequired && isBGLocationAccessNotGranted && isLocationAccessNotGranted) { requestLocationPermission(); } else { setUpBluetooth(); } } private void requestLocationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); alertDialogBuilder .setTitle(getString(R.string.izinkan_lokasi)) .setMessage(getString(R.string.mohon_izinkan_aplikasi_mengakses_lokasi)) .setPositiveButton(getString(R.string.ya), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { makeLocationRequest(); } }); alertDialogBuilder.show(); } else { makeLocationRequest(); } } } private void makeLocationRequest() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{"android.permission.ACCESS_FINE_LOCATION"}, 101); } } // ON REQUEST PERMISSION RESULT @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch(requestCode) { case 101: if (grantResults.length != 0 && grantResults[0] == 0) { setUpBluetooth(); } break; default: Toast.makeText(this, getString(R.string.maaf_izin_lokasi_harus_disetujui), Toast.LENGTH_LONG).show(); } } private void setUpBluetooth() { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); checkBluetooth(bluetoothAdapter); enableBluetooth(this, bluetoothAdapter); bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); scanBLEDevices(bluetoothLeScanner); } // SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java // GET BLUETOOTH ADAPTER private void checkBluetooth (BluetoothAdapter bluetoothAdapter) { if (bluetoothAdapter == null) { Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null"); } else { Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null"); } } // ENABLE BLUETOOTH private void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) { Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled()); if (!bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); Log.d(TAG, "Bluetooth: " + "bluetooth is enabled"); } else { Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled"); } } // SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java // SCAN BLE DEVICES private void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) { showProgressBar(); int SCAN_DURATION = 5000; if(!isScanning) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { isScanning = false; bluetoothLeScanner.stopScan(bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "stop scanning"); populateDataToRecyclerView(); } }, SCAN_DURATION); isScanning = true; showProgressBar(); bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "start scanning"); } else { isScanning = false; bluetoothLeScanner.stopScan(bleScanCallback); Log.d(TAG, "ScanBLEDevices: " + "stop scanning"); populateDataToRecyclerView(); } } private void showProgressBar() { binding.recylerViewFilm.setVisibility(View.GONE); binding.progressBar.setVisibility(View.VISIBLE); } private void hideProgressBar() { binding.recylerViewFilm.setVisibility(View.VISIBLE); binding.progressBar.setVisibility(View.GONE); } private void populateDataToRecyclerView() { hideProgressBar(); scanAndPairAdapter.setBleDeviceEntityList(bleDeviceEntityList); scanAndPairAdapter.notifyDataSetChanged(); binding.recylerViewFilm.setLayoutManager(new LinearLayoutManager(ScanAndPairActivity.this)); binding.recylerViewFilm.setHasFixedSize(true); binding.recylerViewFilm.setAdapter(scanAndPairAdapter); scanAndPairAdapter.setOnItemClickCallback(new ScanAndPairAdapter.OnItemClickCallback() { @Override public void onItemClicked(BLEDeviceEntity bleDeviceEntity) { Toast.makeText( ScanAndPairActivity.this, "try to connect to " + bleDeviceEntity.getName(), Toast.LENGTH_SHORT).show(); } }); } // BLE SCAN CALLBACK private ScanCallback bleScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); String deviceAddress = result.getDevice().getAddress(); String deviceName = result.getDevice().getName(); Log.d(TAG, "ScanCallback, deviceAddress: " + deviceAddress + ", deviceName: " + deviceName); if(bleDeviceEntityList.size() == 0) { bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName)); } else { for(BLEDeviceEntity bleDeviceEntity : bleDeviceEntityList){ if(!bleDeviceEntity.getAddress().equals(deviceAddress)) { bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName)); } } } Log.d(TAG, "bleDeviceEntityList: " + bleDeviceEntityList); } }; // SOURCE FOR LATEST ANDROID BLE APPLICATION SAMPLE: https://github.com/android/connectivity-samples/tree/main/BluetoothAdvertisementsKotlin private static List<ScanFilter> buildScanFilters() { List<ScanFilter> scanFilters = new ArrayList<>(); ScanFilter.Builder builder = new ScanFilter.Builder(); // Comment out the below line to see all BLE devices around you // builder.setServiceUuid(Constants.Service_UUID); scanFilters.add(builder.build()); return scanFilters; } private static ScanSettings buildScanSettings() { ScanSettings.Builder builder = new ScanSettings.Builder(); builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER); return builder.build(); } }
Thank you everyone for your help and special thanks to Isaidamier for showing me the example app here.