From 8a7eb67d72103d0a98c5d620254ec977c64a5eea Mon Sep 17 00:00:00 2001 From: hiar Date: Wed, 13 Aug 2025 16:17:35 +0200 Subject: [PATCH] Migrated to new connection state --- .../android/service/profile/ProfileService.kt | 26 +++++----- .../android/toolbox/profile/data/UiMapper.kt | 3 +- .../profile/viewmodel/ProfileViewModel.kt | 50 +++++++------------ settings.gradle.kts | 8 +-- 4 files changed, 37 insertions(+), 50 deletions(-) diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/profile/ProfileService.kt b/lib_service/src/main/java/no/nordicsemi/android/service/profile/ProfileService.kt index acd1d922..76bbcaeb 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/profile/ProfileService.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/profile/ProfileService.kt @@ -135,7 +135,7 @@ internal class ProfileService : NotificationService() { } } - override fun getConnectionState(address: String): StateFlow? { + override fun connectionState(address: String): StateFlow? { val peripheral = getPeripheralById(address) ?: return null return peripheral.state.also { stateFlow -> connectionJobs[address]?.cancel() @@ -149,14 +149,16 @@ internal class ProfileService : NotificationService() { } } - ConnectionState.Connecting -> _disconnectionReason.tryEmit(null) - is ConnectionState.Disconnected -> { - _disconnectionReason.tryEmit(StateReason(state.reason)) + ConnectionState.Connecting, ConnectionState.Disconnecting -> { + // No action needed, just observing the state } - ConnectionState.Closed -> return@onEach - - ConnectionState.Disconnecting -> { + is ConnectionState.Disconnected -> { + if (state.reason == null) { + _disconnectionReason.tryEmit(null) + return@onEach + } else + _disconnectionReason.tryEmit(StateReason(state.reason!!)) connectionJobs[address]?.cancel() handleDisconnection(address) } @@ -185,7 +187,6 @@ internal class ProfileService : NotificationService() { centralManager.connect(peripheral, options = ConnectionOptions.Direct()) } catch (e: Exception) { Timber.e(e, "Failed to connect to the ${peripheral.address}") - stopForegroundService() // Stop service if connection fails } } @@ -221,7 +222,6 @@ internal class ProfileService : NotificationService() { ) } catch (e: Exception) { Timber.tag("ObserveServices").e(e) - handleDisconnection(peripheral.address) } } } @@ -261,13 +261,13 @@ internal class ProfileService : NotificationService() { /** * Handle disconnection and cleanup for the given peripheral. */ - private fun handleDisconnection(peripheral: String) { + private fun handleDisconnection(device: String) { val currentDevices = _connectedDevices.value.toMutableMap() - currentDevices[peripheral]?.let { - currentDevices.remove(peripheral) + currentDevices[device]?.let { + currentDevices.remove(device) _connectedDevices.tryEmit(currentDevices) } - clearJobs(peripheral) + clearJobs(device) clearFlags() stopServiceIfNoDevices() } diff --git a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/data/UiMapper.kt b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/data/UiMapper.kt index b49a403f..359d9406 100644 --- a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/data/UiMapper.kt +++ b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/data/UiMapper.kt @@ -5,7 +5,7 @@ import no.nordicsemi.kotlin.ble.core.ConnectionState.Disconnected.Reason fun toReason(reason: Reason): String = when (reason) { Reason.Cancelled -> "Connection was cancelled." - Reason.LinkLoss -> "Device signal has been lost." + Reason.LinkLoss -> "The device got out of range or has turned off." Reason.Success -> "Device disconnected successfully." Reason.TerminateLocalHost -> "Device disconnected by the local host." Reason.TerminatePeerUser -> "Device disconnected by the peer user." @@ -13,4 +13,5 @@ fun toReason(reason: Reason): String = is Reason.Unknown -> "Oops...! Connection went on a coffee break." Reason.UnsupportedAddress -> "Device disconnected due to unsupported address." Reason.InsufficientAuthentication -> "Device disconnected due to insufficient authentication." + Reason.UnsupportedConfiguration -> "Connection attempt was aborted due PHY negotiations failure." } \ No newline at end of file diff --git a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/viewmodel/ProfileViewModel.kt b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/viewmodel/ProfileViewModel.kt index 9e1bf637..3791b195 100644 --- a/profile/src/main/java/no/nordicsemi/android/toolbox/profile/viewmodel/ProfileViewModel.kt +++ b/profile/src/main/java/no/nordicsemi/android/toolbox/profile/viewmodel/ProfileViewModel.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach @@ -23,14 +22,12 @@ import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel import no.nordicsemi.android.log.LogSession import no.nordicsemi.android.log.timber.nRFLoggerTree -import no.nordicsemi.android.service.profile.CustomReason import no.nordicsemi.android.service.profile.ProfileServiceManager import no.nordicsemi.android.service.profile.ServiceApi import no.nordicsemi.android.service.profile.StateReason import no.nordicsemi.android.toolbox.profile.ProfileDestinationId import no.nordicsemi.android.toolbox.profile.R import no.nordicsemi.android.toolbox.profile.repository.DeviceRepository -import no.nordicsemi.android.ui.view.internal.DisconnectReason import no.nordicsemi.kotlin.ble.client.android.Peripheral import no.nordicsemi.kotlin.ble.core.ConnectionState import timber.log.Timber @@ -148,8 +145,7 @@ internal class ProfileViewModel @Inject constructor( isAlreadyConnected: Boolean ) { // Drop the first default state (Closed) before connection. - job = api.getConnectionState(deviceAddress) - ?.drop(if (isAlreadyConnected) 0 else 1) + job = api.connectionState(deviceAddress) ?.onEach { connectionState -> if (peripheral == null) peripheral = api.getPeripheralById(address) when (connectionState) { @@ -167,34 +163,24 @@ internal class ProfileViewModel @Inject constructor( } is ConnectionState.Disconnected -> { - _deviceState.update { - DeviceConnectionState.Disconnected( - peripheral, - StateReason(connectionState.reason) - ) - }.also { - // Remove the analytics logged profiles for the disconnected device. - deviceRepository.removeLoggedProfile(deviceAddress) - } - } - - ConnectionState.Closed -> { - unbindService() - api.disconnectionReason.onEach { reason -> - if (reason != null) { - _deviceState.update { - DeviceConnectionState.Disconnected(peripheral, reason) - } - } else { - _deviceState.update { - DeviceConnectionState.Disconnected( - peripheral, - CustomReason(DisconnectReason.UNKNOWN) - ) - } + // If disconnected reason is null, it means that the connection was never initiated. + if (connectionState.reason == null) { + _deviceState.update { + DeviceConnectionState.Idle } - }.launchIn(viewModelScope) - job?.cancel() + return@onEach + } else { + _deviceState.update { + DeviceConnectionState.Disconnected( + peripheral, + StateReason(connectionState.reason!!) + ) + }.also { + // Remove the analytics logged profiles for the disconnected device. + deviceRepository.removeLoggedProfile(deviceAddress) + } + job?.cancel() + } } ConnectionState.Connecting -> { diff --git a/settings.gradle.kts b/settings.gradle.kts index 5e161979..277b6ae7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:2.9-1") + from("no.nordicsemi.android.gradle:version-catalog:2.9-2") } } } @@ -69,9 +69,9 @@ include(":profile_data") include(":profile_manager") include(":permissions-ranging") -if (file("../Android-Common-Libraries").exists()) { - includeBuild("../Android-Common-Libraries") -} +//if (file("../Android-Common-Libraries").exists()) { +// includeBuild("../Android-Common-Libraries") +//} // //if (file("../Kotlin-BLE-Library").exists()) { // includeBuild("../Kotlin-BLE-Library")