I am trying to implement the most simple app to scan for a BLE device. I have given the necessary rights:
<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" />
However, onScanResult never gets called. Bluetooth is enabled on my phone and the BLE device is on, this is my code:
class MainActivity : AppCompatActivity() {
private val bleScanner = object :ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
Log.d("DeviceListActivity", "onScanResult()")
super.onScanResult(callbackType, result)
val device = result.device
Log.d("DeviceScanner", "Device found: ${device.address} - ${device.name ?: "Unknown"}")
//Log.d("DeviceListActivity","onScanResult: ${result.device?.address} - ${result.device?.name}")
}
}
private val bluetoothLeScanner: BluetoothLeScanner
get() {
val bluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
return bluetoothAdapter.bluetoothLeScanner
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
Log.d("DeviceListActivity", "onCreate()")
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_device_list)
}
override fun onStart() {
Log.d("DeviceListActivity", "onStart()")
super.onStart()
enableLocation()
bluetoothLeScanner.startScan(bleScanner)
}
override fun onStop() {
Log.d("DeviceListActivity", "onStop()")
bluetoothLeScanner.stopScan(bleScanner)
super.onStop()
}
private fun enableLocation(): Boolean {
val service = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val enabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER)
return enabled
}
}
What am I missing?
Yes it was the missing requested permission. I inserted following function into onResume()
if (ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
0
)
} else {
leDeviceScanner.start()
}
It now workes like a charm. Thanks!
Related
I have a problem with my bluetooth scanner.I'm using a samsung A50 with android 11 .So my Bluetooth is supposed to check the surrounding bluetooth devices and then add them into an ArrayList and display it on screen with a recyclerview. The problem is I don't find any device. I tried to debug my code and It seems that my code doesn't go to mReceiver that's why I don't detect any device. The code was working last week I found the devices (I found the same device multiple times it's normal) but they didn't add to the ArrayList. So Now I'm asking you guys here is my code.
I'm pretty sure I gave all the permissions necessary and even more and I also given the location permission to my app.
This is The Activity:
class ConnectionActivity : AppCompatActivity(),BluetoothOnItemClickListener{
private lateinit var binding : ActivityConnectionBinding
private lateinit var manager : RecyclerView.LayoutManager
val dataset = Datasource().loadDataBluetooth()
private val devices_list : ArrayList<BluetoothDevice> = ArrayList()
var m_bluetoothAdapter : BluetoothAdapter? = null
val REQUEST_ENABLE_BLUETOOTH = 1
companion object{
val EXTRA_ADDRESS :String = "Device_address"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityConnectionBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = "Bluetooth"
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if(m_bluetoothAdapter == null){
Toast.makeText(this, "not supported", Toast.LENGTH_SHORT).show()
return
}
if (!m_bluetoothAdapter!!.isEnabled){
val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBluetoothIntent,REQUEST_ENABLE_BLUETOOTH)
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivity(discoverableIntent)
}else{
discoverDevices()}
}
private val mReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (BluetoothDevice.ACTION_FOUND == action) {
// A Bluetooth device was found
// Getting device information from the intent
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
if (device != null) {
devices_list.add(device)
println("SIZE : ${devices_list.size}")
}
}
}
}
private fun discoverDevices(){
if (m_bluetoothAdapter!!.isDiscovering) {
// Bluetooth is already in mode discovery mode, we cancel to restart it again
m_bluetoothAdapter!!.cancelDiscovery()
}
val bool = m_bluetoothAdapter?.startDiscovery()
Log.i("", bool.toString())
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
registerReceiver(mReceiver, IntentFilter(BluetoothDevice.ACTION_FOUND))
println("Count : ${devices_list.size}")
manager = LinearLayoutManager(this)
binding.recycleView.adapter = ItemAdapter(devices_list,this)
binding.recycleView.layoutManager = manager
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
if (resultCode == Activity.RESULT_OK) {
if (m_bluetoothAdapter!!.isEnabled) {
Toast.makeText(this, "Bluetooth enabled", Toast.LENGTH_SHORT).show()
discoverDevices()
} else {
Toast.makeText(this, "Bluetooth disabled", Toast.LENGTH_SHORT).show()
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "Bluetooth enabling has been canceled", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.menu_connection, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.ic_ip -> Toast.makeText(this, "IP", Toast.LENGTH_SHORT).show()
}
return super.onOptionsItemSelected(item)
}
override fun onStop() {
super.onStop()
unregisterReceiver(mReceiver)
}
}
And This is the Adapter :
class ItemAdapter(private val devices : ArrayList<BluetoothDevice>, var clickListener: BluetoothOnItemClickListener): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>(){
inner class ItemViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item : BluetoothDevice,action: BluetoothOnItemClickListener){
binding.item?.StringNameB = item.name
itemView.setOnClickListener {
binding.item?.checked = true
notifyItemChanged(bindingAdapterPosition)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
val adapterlayout = ListItemBinding.inflate(inflater,parent,false)
return ItemViewHolder(adapterlayout)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(devices[position],clickListener)
holder.binding.executePendingBindings()
}
override fun getItemCount(): Int {
return devices.size
}
}
interface BluetoothOnItemClickListener{
}
And this is my the permissions I have in my manifest
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
You also need to request permissions at runtime. Have a look at the links below for more information:-
Bluetooth scanner not discovering devices
Turn on LE scanning without asking for user permission
How to request location permission at runtime
The ultimate guide to Android BLE
On Button Click i could make a call using Intent -> Action Call. Later in the code i implemented Broadcast Received. I found that once BroadcasteReceiver is registered Calls using Intent->action.Call is not process. Kindly help.
// This function is called on button click
fun callPerson(position: Int) {
val callObject = allCallList?.get(position)
try {
var startIndent = Intent(Intent.ACTION_CALL)
startIndent.data = Uri.parse("tel:" + callObject?.number)
ContextCompat.startActivity(mContext as Context, startIndent, null)
}
catch(ex : Exception) {
Toast.makeText(mContext,Toast.message.toString(),Toast.LENGTH_LONG).show()
}
}
//Permission taken
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SEND_SMS" />
//Service
class CallCaptureService : Service()
{
private var callCapture = CallCapture()
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
override fun onCreate() {
super.onCreate()
var intentFilter = IntentFilter()
intentFilter.addAction("android.intent.action.PHONE_STATE");
intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
intentFilter?.priority = 100
registerReceiver(callCapture, intentFilter)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(callCapture)
}
}
//CallCapture is receiver
class CallCapture : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent?.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
Staticated.savedNumber = intent?.getExtras()?.getString("android.intent.extra.PHONE_NUMBER")
} else {
val stateStr = intent?.getExtras()?.getString(TelephonyManager.EXTRA_STATE)
val number = intent?.getExtras()?.getString(TelephonyManager.EXTRA_INCOMING_NUMBER)
var state = 0
if (stateStr == TelephonyManager.EXTRA_STATE_IDLE) {
state = TelephonyManager.CALL_STATE_IDLE
} else if (stateStr == TelephonyManager.EXTRA_STATE_OFFHOOK) {
state = TelephonyManager.CALL_STATE_OFFHOOK
} else if (stateStr == TelephonyManager.EXTRA_STATE_RINGING) {
state = TelephonyManager.CALL_STATE_RINGING
}
onCallStateChanged(context, state, number)
}
}
In the physical device, it takes too much time to call. But in avd it's run perfectly. I test this code in redmi note 4 and zuk z2 plus. Both device location set on HIGH_ACCURACY . I want to fetch the location rapidly. But it takes too much time to execute.
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0f,wcLoactionListener)
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0f,wcLoactionListener)
But if I removed this line
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0f,wcLoactionListener)
then onLocationChanged() not calling.
Here is my location listener
class MyListener:LocationListener {
override fun onLocationChanged(location: Location?) {
Log.d("Loc","*************************************************** "+ location)
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
}
override fun onProviderEnabled(provider: String?) {
}
override fun onProviderDisabled(provider: String?) {
}
}
Mainactivity:-
class MainActivity : AppCompatActivity() {
val LOCATION_CODE = 212
private lateinit var locationManager: LocationManager
private lateinit var cwcLoactionListener:MyListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cwcLoactionListener = MyListener()
checkLocationPermission()
}
#SuppressLint("MissingPermission")
fun fechLOc(){
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0f,cwcLoactionListener)
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,500,0f,cwcLoactionListener)
}
private fun checkLocationPermission() {
if(Build.VERSION.SDK_INT>=23){
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED ){
requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_CODE)
return
}
}
fechLOc()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
LOCATION_CODE->{
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this,"Accept this permission", Toast.LENGTH_LONG).show()
}else{
fechLOc()
}
}
else ->{
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}
}
manifest
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
I'm trying to list all BLE devices on an Android device, using Kotlin (the Java-version don't work either) but I don't get any devices or any call back at all, except for a "scan was already started"
I have the correct uses-permission in the manifest.
Here is the current minimum of code, I'm trying with.
But even the sample code from Google is listing any devices.
I'm running on a Pixel with Android version 8.1.0.
I have it working on iOS, with the basic BLE device list (Swift)!
private val bleScanner = object :ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult?) {
super.onScanResult(callbackType, result)
Log.d("DeviceListActivity","onScanResult: ${result?.device?.address} - ${result?.device?.name}")
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)
Log.d("DeviceListActivity","onBatchScanResults:${results.toString()}")
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
Log.d("DeviceListActivity", "onScanFailed: $errorCode")
}
}
private val bluetoothLeScanner: BluetoothLeScanner
get() {
val bluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter = bluetoothManager.adapter
return bluetoothAdapter.bluetoothLeScanner
}
class ListDevicesAdapter(context: Context?, resource: Int) : ArrayAdapter<String>(context, resource)
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("DeviceListActivity", "onCreate()")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_device_list)
}
override fun onStart() {
Log.d("DeviceListActivity","onStart()")
super.onStart()
bluetoothLeScanner.startScan(bleScanner)
}
override fun onStop() {
bluetoothLeScanner.stopScan(bleScanner)
super.onStop()
}
You need to declare permission:
uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
and you for devices with android 6.0 + request manually this permission:
override fun onStart() {
Log.d("ScanDeviceActivity", "onStart()")
super.onStart()
when (PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
PackageManager.PERMISSION_GRANTED -> bluetoothLeScanner.startScan(bleScanner)
else -> requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 1)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
1 -> when (grantResults) {
intArrayOf(PackageManager.PERMISSION_GRANTED) -> {
Log.d("ScanDevices", "onRequestPermissionsResult(PERMISSION_GRANTED)")
bluetoothLeScanner.startScan(bleScanner)
}
else -> {
Log.d("ScanDevices", "onRequestPermissionsResult(not PERMISSION_GRANTED)")
}
}
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
I need to scan for a list of wifi access points in my android app. I've done it in the past using java, but I'm having trouble getting my kotlin code to work.
My code:
var resultList = ArrayList<ScanResult>()
lateinit var wifiManager: WifiManager
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
resultList = wifiManager.scanResults as ArrayList<ScanResult>
Log.d("TESTING", "onReceive Called")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wifiManager = this.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
override fun onGridTileClicked(x: Int, y: Int) {
startScanning()
}
fun startScanning() {
registerReceiver(broadcastReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
Handler().postDelayed({
stopScanning()
}, 10000)
}
fun stopScanning() {
unregisterReceiver(broadcastReceiver)
val axisList = ArrayList<Axis>()
for (result in resultList) {
axisList.add(Axis(result.BSSID, result.level))
}
Log.d("TESTING", axisList.toString())
}
The onReceive() function is never called, and I have the ACCESS_FINE_LOCATION and ACCESS_WIFI_STATE both declared in the manifest, so I'm not sure what I'm doing wrong. I'm sure I'm missing something obvious, but some help would be appreciated. Thanks!
You forgot to start scanning. Add wifiManager.startScan() call in your startScanning method.