How to Successfully Scan for Bluetooth Android Devices with Low Energy

The Use Case

Scan for all available Android devices supporting BLE, Use any Android device with Android OS starting from 4 to 10 to Scan.

The Problem

1. Successful scanning has inconsistent development requirements over different Android OS2. Official docs are outdated and don’t mention these inconsistencies explicitly, also samples accompanying the docs are using deprecated APIs

Article objectives

This is not a tutorial! this article covers the missing and confusing parts in the Android Developers overview article on Bluetooth Low Energy. It is highly recommended reading.

Technical Solution In Three Steps

1. The first problem I faced was that I needed to make sure that the device’s Bluetooth is not only On but also Visible. That was mentioned explicitly in the documentation but after thorough readings!

Findings: no need to request Bluetooth permission if discoverability is requested, also to stay discoverable infinitely through development set EXTRA_DISCOVERABLE_DURATION to 0, but that was not recommended in real use cases by docs

[pastacode lang="markup" manual="val%20discoverableIntent%20%3DIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)%0AdiscoverableIntent%0A%20%20.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION%2C0%2F*set%20a%20duration%2C%200%20is%20not%20recommended*%2F)%0AstartActivity(discoverableIntent)" message="" highlight="" provider="manual"/]

2. If your app targets Android 6+, you must not only declare ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION in the Manifest, but also request Runtime Permissions in your app, or while in development mode, set your location permissions on for your app explicitly. If you didn’t, it would never be discoverable or even discover other devices.

A- In development mode give permission manually through settings.

[caption id="attachment_70995" align="aligncenter" width="250"]

navigation menu

App permissions settings[/caption]B- or addRuntime Permissionsto activate it momentarily once user asks for scanning[pastacode lang="markup" manual="private%20fun%20allowLocationDetectionPermissions()%20%7B%0A%20%20%20%20%20if%20(ContextCompat.checkSelfPermission(this%40MainActivity%2C%20Manifest.permission.ACCESS_FINE_LOCATION)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%3D%20PackageManager.PERMISSION_DENIED)%20%7B%0A%20%20%20%20%20%20%20%20%20ActivityCompat.requestPermissions(this%40MainActivity%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)%2C%20FINE_LOCATION_PERMISSION_REQUEST)%0A%20%20%20%20%20%7D%0A%7D%0A%0Aoverride%20fun%20onRequestPermissionsResult(requestCode%3A%20Int%2C%20permissions%3A%20Array%3CString%3E%2C%20grantResults%3A%20IntArray)%20%7B%0A%20%20%20%20%20when%20(requestCode)%20%7B%0A%20%20%20%20%20%20%20%20%20FINE_LOCATION_PERMISSION_REQUEST%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20if%20((grantResults.isNotEmpty()%20%26%26%20grantResults%5B0%5D%20%3D%3D%20PackageManager.PERMISSION_GRANTED))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scanLeDevice(true)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Fnotify%20the%20user%20to%20allow%20location%20detection%20otherwise%20the%20scaning%20won't%20work%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20return%0A%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%7D%0A%7D" message="" highlight="" provider="manual"/]

3. Please use latest BluetoothLeScanner API to start and end scanning instead of bluetoothAdapter instance, this was the outdated part in the docs

[pastacode lang="markup" manual="private%20fun%20scanLeDevice(enable%3A%20Boolean)%20%7B%0A%20%20%20%20%20%20%20%20when%20(enable)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20true%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Stops%20scanning%20after%20a%20pre-defined%20scan%20period.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Handler().postDelayed(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20false%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.stopScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%205000)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20true%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.startScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mScanning%20%3D%20false%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bluetoothAdapter%3F.bluetoothLeScanner%3F.stopScan(mLeScanCallback)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%7D%0A%0A%20private%20var%20mLeScanCallback%3A%20ScanCallback%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20object%20%3A%20ScanCallback()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onScanResult(callbackType%3A%20Int%2C%20result%3A%20ScanResult%3F)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onScanResult(callbackType%2C%20result)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onBatchScanResults(results%3A%20List%3CScanResult%3F%3E%3F)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onBatchScanResults(results)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20override%20fun%20onScanFailed(errorCode%3A%20Int)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super.onScanFailed(errorCode)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2FDo%20your%20thing%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D" message="" highlight="" provider="manual"/]

Then voila! All devices from Android 4 to 10 will be scannable in your app.

Please, check the full sample app code here

Related posts

The latest articles from Andela.

Visit our blog

Customer-obsessed? 4 Steps to improve your culture

If you get your team's culture right, you can create processes that will allow you to operationalize useful new technologies. Check out our 4 steps to transform your company culture.

How to Build a RAG-Powered LLM Chat App with ChromaDB and Python

Harness the power of retrieval augmented generation (RAG) and large language models (LLMs) to create a generative AI app. Andela community member Oladimeji Sowole explains how.

Navigating the future of work with generative AI and stellar UX design

In this Writer's Room blog, Carlos Tay discusses why ethical AI and user-centric design are essential in shaping a future where technology amplifies human potential.

We have a 96%+
talent match success rate.

The Andela Talent Operating Platform provides transparency to talent profiles and assessment before hiring. AI-driven algorithms match the right talent for the job.