feat: show scan results for Kirby devices in list

This commit is contained in:
Fabian Christoffel
2023-06-16 15:14:42 +02:00
parent 47d945e398
commit e6850d78d8
6 changed files with 171 additions and 32 deletions

View File

@@ -22,9 +22,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.example.sensortestingapp.databinding.ActivityMainBinding
import java.util.stream.Collectors
import java.util.stream.Collectors.toList
private const val ENABLE_BLUETOOTH_REQUEST_CODE = 1
@@ -70,7 +72,13 @@ class MainActivity : AppCompatActivity() {
}
}
private var scanResults = HashMap<String, ScanResult>();
private var kirbyScanResults = ArrayList<ScanResult>();
private val scanResultAdapter: ScanResultAdapter by lazy {
ScanResultAdapter(kirbyScanResults) {
// TODO: Implement
}
}
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
@@ -82,6 +90,7 @@ class MainActivity : AppCompatActivity() {
setSupportActionBar(binding.toolbar)
binding.fab.setOnClickListener { view ->
if (!isScanning) {
startBleScan()
@@ -92,6 +101,26 @@ class MainActivity : AppCompatActivity() {
// .setAnchorView(R.id.fab)
// .setAction("Action", null).show()
}
setupRecyclerView()
}
private fun setupRecyclerView() {
binding.scanResultsRecyclerView.apply {
adapter = scanResultAdapter
layoutManager = LinearLayoutManager(
this@MainActivity,
RecyclerView.VERTICAL,
false
)
isNestedScrollingEnabled = false
}
val animator = binding.scanResultsRecyclerView.itemAnimator
if (animator is SimpleItemAnimator) {
animator.supportsChangeAnimations = false
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -178,18 +207,25 @@ class MainActivity : AppCompatActivity() {
@SuppressLint("MissingPermission")
val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
scanResults.merge(result.device.address, result, fun(o, n): ScanResult {
return if (n.rssi >= o.rssi) n else o;
})
val displayScanResults = getDisplayScanResults();
Log.i(
Log.d(
"ScanCallback",
"Found BLE device ${result.device.name ?: result.device.address} (rssi: ${result.rssi}). Current scan results (${displayScanResults.size}): ${
toDebugScanResults(displayScanResults)
}"
"Found BLE device with address ${result.device.address} (name: ${result.device.name}, rssi: ${result.rssi})"
)
if ((result.device.name ?: "").lowercase().contains("kirby")) {
Log.i(
"ScanCallback",
"Found Kirby device with name ${result.device.name} (address: ${result.device.address}, rssi: ${result.rssi})"
)
val index =
kirbyScanResults.indexOfFirst { r -> r.device.address == result.device.address }
if (index == -1) {
kirbyScanResults.add(result);
scanResultAdapter.notifyItemInserted(index)
} else {
kirbyScanResults[index] = result
scanResultAdapter.notifyItemChanged(index)
}
}
}
override fun onScanFailed(errorCode: Int) {
@@ -197,25 +233,12 @@ class MainActivity : AppCompatActivity() {
}
}
private fun getDisplayScanResults(): List<ScanResult> {
return scanResults.values.stream()
.filter { r -> r.rssi > -80 }
.sorted { r1, r2 -> Integer.compare(r2.rssi, r1.rssi) }
.collect(toList())
}
private fun toDebugScanResults(scanResults: List<ScanResult>): String {
return scanResults.stream()
.map { r -> "${r.device.name ?: r.device.address} (${r.rssi})" }
.collect(Collectors.joining(", "))
}
private fun startBleScan() {
if (!hasRequiredRuntimePermissions()) {
requestRelevantRuntimePermissions()
} else {
scanResults.clear()
kirbyScanResults.clear()
scanResultAdapter.notifyDataSetChanged()
bleScanner.startScan(null, scanSettings, scanCallback)
isScanning = true
}

View File

@@ -0,0 +1,64 @@
package com.example.sensortestingapp
import android.annotation.SuppressLint
import android.bluetooth.le.ScanResult
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
@SuppressLint("MissingPermission")
class ScanResultAdapter(
private val items: List<ScanResult>,
private val onClickListener: ((device: ScanResult) -> Unit)
) : RecyclerView.Adapter<ScanResultAdapter.ViewHolder>() {
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder)
*/
class ViewHolder(
private val view: View,
private val onClickListener: ((device: ScanResult) -> Unit)
) : RecyclerView.ViewHolder(view) {
val deviceNameView: TextView
val macAddressView: TextView
val signalStrengthView: TextView
init {
deviceNameView = view.findViewById(R.id.device_name)
macAddressView = view.findViewById(R.id.mac_address)
signalStrengthView = view.findViewById(R.id.signal_strength)
}
fun bind(result: ScanResult) {
deviceNameView.text = result.device.name ?: "Unnamed"
macAddressView.text = result.device.address
signalStrengthView.text = "${result.rssi} dBm"
view.setOnClickListener { onClickListener.invoke(result) }
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
// Create a new view, which defines the UI of the list item
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.row_scan_result, viewGroup, false)
return ViewHolder(view, onClickListener)
}
override fun getItemCount() = items.size
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
val item = items[position]
holder.bind(item)
}
}