fix: properly handle connecting to bonded devices

This commit is contained in:
Stefan Zollinger
2024-04-11 15:13:16 +02:00
parent 7311ba335f
commit 62ce221860
5 changed files with 72 additions and 16 deletions

View File

@@ -59,6 +59,7 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.0' implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.code.gson:gson:2.8.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
implementation group: 'commons-codec', name: 'commons-codec', version: '1.16.0' implementation group: 'commons-codec', name: 'commons-codec', version: '1.16.0'

View File

@@ -394,9 +394,11 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
@Synchronized @Synchronized
private fun enqueueOperation(operation: BleOperationType) { private fun enqueueOperation(operation: BleOperationType) {
/*
if (isScanning) { if (isScanning) {
stopScan() stopScan()
} }
*/
operationQueue.add(operation) operationQueue.add(operation)
notifyListenersOfQueueChange(operation.device.address) notifyListenersOfQueueChange(operation.device.address)
if (pendingOperation == null) { if (pendingOperation == null) {

View File

@@ -84,6 +84,7 @@ object DecoderIaq {
} }
data class Measurement ( data class Measurement (
var deviceId: String? = null,
var msgType: Number? = null, var msgType: Number? = null,
var co2: Number? = null, var co2: Number? = null,
var voc: Number? = null, var voc: Number? = null,
@@ -110,8 +111,5 @@ object DecoderIaq {
'}' '}'
} }
fun toCsv() : String {
return "${msgType},${co2},${voc},${humidity},${temperature},${pressure}${pm25},${pm10},${occupancy}"
}
} }
} }

View File

@@ -45,16 +45,18 @@ class KirbyDevice(
private val queue: RequestQueue = Volley.newRequestQueue(context) private val queue: RequestQueue = Volley.newRequestQueue(context)
fun subscribe() { fun subscribe() {
if(statuses.contains(DeviceStatus.CONNECTED)) {
connectionManager.enableNotification( connectionManager.enableNotification(
bleDevice, SERVICE_UUID, CHAR_UUID bleDevice, SERVICE_UUID, CHAR_UUID
) )
}
} }
fun readIaq() { fun readIaq() {
connectionManager.readChar(bleDevice, SERVICE_UUID, CHAR_UUID) connectionManager.readChar(bleDevice, SERVICE_UUID, CHAR_UUID)
} }
override fun onSuccessfulCharRead( override fun onSuccessfulCharRead(
gatt: BluetoothGatt, gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic characteristic: BluetoothGattCharacteristic
@@ -66,13 +68,20 @@ class KirbyDevice(
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
rssi = result.rssi rssi = result.rssi
onStateChange(this) onStateChange(this)
if(result.isConnectable) {
connectionManager.connect(bleDevice)
connectionManager.discoverServices(bleDevice)
}
} }
override fun onConnect(gatt: BluetoothGatt) { override fun onConnect(gatt: BluetoothGatt) {
statuses.add(DeviceStatus.CONNECTED) statuses.add(DeviceStatus.CONNECTED)
statuses.remove(DeviceStatus.MISSING) statuses.remove(DeviceStatus.MISSING)
subscribe()
onStateChange(this) onStateChange(this)
} }
override fun onConnectToBondedFailed(gatt: BluetoothGatt) { override fun onConnectToBondedFailed(gatt: BluetoothGatt) {
@@ -136,12 +145,13 @@ class KirbyDevice(
if (measurement == null) { if (measurement == null) {
payload = Payload(hexPayload) payload = Payload(hexPayload)
} else { } else {
measurement.deviceId = bleDevice.address
payload = Payload(measurement.toString()) payload = Payload(measurement.toString())
Log.i("BleListener", "Char received: $payload") Log.i("BleListener", "Char received: $payload")
val base64Payload = Base64.getEncoder().encodeToString(characteristic.value) val base64Payload = Base64.getEncoder().encodeToString(characteristic.value)
publishMeasurement(base64Payload) publishMeasurement(base64Payload)
loggerDb.writeLog( measurement) loggerDb.writeLog(measurement)
} }
measurements.add(payload) measurements.add(payload)
@@ -155,6 +165,11 @@ class KirbyDevice(
private fun publishMeasurement(payload: String) { private fun publishMeasurement(payload: String) {
val accessKey = BuildConfig.API_KEY val accessKey = BuildConfig.API_KEY
val url = BuildConfig.API_BASE_URL val url = BuildConfig.API_BASE_URL
if(url.isEmpty()) {
return
}
val eui = "0000${bleDevice.address.replace(":", "")}" val eui = "0000${bleDevice.address.replace(":", "")}"
val postData = JSONObject() val postData = JSONObject()

View File

@@ -15,20 +15,29 @@ import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View.INVISIBLE import android.view.View.INVISIBLE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.logitech.vc.kirbytest.databinding.ActivityMainBinding import com.logitech.vc.kirbytest.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -108,12 +117,33 @@ class MainActivity : AppCompatActivity() {
setupDevicesList() setupDevicesList()
createFileLauncher = createFileLauncher =
registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri: Uri? -> registerForActivityResult(CreateDocument("text/csv")) { uri: Uri? ->
uri?.let { uri?.let {
// Use the URI to write your CSV content // Use the URI to write your CSV content
loggerDb.exportToUri(uri) CoroutineScope(Dispatchers.IO).launch {
loggerDb.exportToUri(uri)
withContext(Dispatchers.Main) {
Toast.makeText(
this@MainActivity,
"Log export completed.",
Toast.LENGTH_SHORT
).show()
}
}
} }
} }
connectionManager.startScan()
lifecycleScope.launch {
withContext(Dispatchers.Main) {
delay(5000L)
connectionManager.stopScan()
}
}
} }
private fun toggleScanning(): Unit { private fun toggleScanning(): Unit {
@@ -156,11 +186,11 @@ class MainActivity : AppCompatActivity() {
private fun addBondedDevices(): Unit { private fun addBondedDevices(): Unit {
bluetoothAdapter.bondedDevices.filter { isKirbyDevice(it) }.forEach { bluetoothAdapter.bondedDevices.filter { isKirbyDevice(it) }.forEach {
val kirbyDevice = newKirbyDevice(it) newKirbyDevice(it)
kirbyDevice.subscribe()
} }
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun addDummyDevices() { private fun addDummyDevices() {
for (i in 0..14) { for (i in 0..14) {
@@ -183,7 +213,7 @@ class MainActivity : AppCompatActivity() {
} }
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
if (isKirbyDevice(result.device)) { if (isKirbyDevice(result.device) && result.isConnectable) {
Log.i( Log.i(
"ScanCallback", "ScanCallback",
@@ -200,7 +230,10 @@ class MainActivity : AppCompatActivity() {
return mngr return mngr
} }
private fun newKirbyDevice(bleDevice: BluetoothDevice): KirbyDevice { private fun newKirbyDevice(
bleDevice: BluetoothDevice,
autoConnect: Boolean = false
): KirbyDevice {
val device = KirbyDevice(this.applicationContext, connectionManager, bleDevice, loggerDb) { val device = KirbyDevice(this.applicationContext, connectionManager, bleDevice, loggerDb) {
val i = kirbyDevices.indexOfFirst { d -> d === it } val i = kirbyDevices.indexOfFirst { d -> d === it }
@@ -208,9 +241,16 @@ class MainActivity : AppCompatActivity() {
deviceListAdapter.notifyItemChanged(i) deviceListAdapter.notifyItemChanged(i)
} }
} }
Log.i("MainActivity", bleDevice.address)
connectionManager.register(device) connectionManager.register(device)
connectionManager.connect(bleDevice)
connectionManager.discoverServices(bleDevice) if (autoConnect) {
connectionManager.connect(bleDevice)
connectionManager.discoverServices(bleDevice)
}
kirbyDevices.add(device) kirbyDevices.add(device)
deviceListAdapter.notifyItemInserted(kirbyDevices.size - 1) deviceListAdapter.notifyItemInserted(kirbyDevices.size - 1)
return device return device