mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2026-01-04 15:24:19 +01:00
Add Link Loss support for prx
This commit is contained in:
@@ -3,3 +3,16 @@ package no.nordicsemi.android.service
|
||||
enum class BleManagerStatus {
|
||||
CONNECTING, OK, DISCONNECTED
|
||||
}
|
||||
|
||||
enum class BleServiceStatus {
|
||||
CONNECTING, OK, DISCONNECTED, LINK_LOSS;
|
||||
|
||||
fun mapToSimpleManagerStatus(): BleManagerStatus {
|
||||
return when (this) {
|
||||
CONNECTING -> BleManagerStatus.CONNECTING
|
||||
OK -> BleManagerStatus.OK
|
||||
DISCONNECTED -> BleManagerStatus.DISCONNECTED
|
||||
LINK_LOSS -> BleManagerStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import no.nordicsemi.android.ble.BleManager
|
||||
import no.nordicsemi.android.ble.observer.ConnectionObserver
|
||||
import no.nordicsemi.android.log.ILogSession
|
||||
import no.nordicsemi.android.log.Logger
|
||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
||||
@@ -44,7 +45,7 @@ abstract class BleProfileService : Service() {
|
||||
|
||||
protected abstract val manager: BleManager
|
||||
|
||||
private val _status = MutableStateFlow(BleManagerStatus.CONNECTING)
|
||||
private val _status = MutableStateFlow(BleServiceStatus.CONNECTING)
|
||||
val status = _status.asStateFlow()
|
||||
|
||||
/**
|
||||
@@ -71,20 +72,22 @@ abstract class BleProfileService : Service() {
|
||||
manager.setConnectionObserver(object : ConnectionObserverAdapter() {
|
||||
override fun onDeviceConnected(device: BluetoothDevice) {
|
||||
super.onDeviceConnected(device)
|
||||
_status.value = BleManagerStatus.OK
|
||||
_status.value = BleServiceStatus.OK
|
||||
}
|
||||
|
||||
override fun onDeviceFailedToConnect(device: BluetoothDevice, reason: Int) {
|
||||
super.onDeviceFailedToConnect(device, reason)
|
||||
_status.value = BleManagerStatus.DISCONNECTED
|
||||
_status.value = BleServiceStatus.DISCONNECTED
|
||||
stopSelf()
|
||||
scope.close()
|
||||
}
|
||||
|
||||
override fun onDeviceDisconnected(device: BluetoothDevice, reason: Int) {
|
||||
super.onDeviceDisconnected(device, reason)
|
||||
_status.value = BleManagerStatus.DISCONNECTED
|
||||
scope.close()
|
||||
if (reason == ConnectionObserver.REASON_LINK_LOSS) {
|
||||
_status.value = BleServiceStatus.LINK_LOSS
|
||||
} else {
|
||||
_status.value = BleServiceStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -98,7 +101,7 @@ abstract class BleProfileService : Service() {
|
||||
*
|
||||
* @return true to use autoConnect feature, false (default) otherwise.
|
||||
*/
|
||||
private fun shouldAutoConnect(): Boolean {
|
||||
protected open fun shouldAutoConnect(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.cgms.data.CGMRepository
|
||||
import no.nordicsemi.android.cgms.data.CGMServiceCommand
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
@@ -21,7 +22,11 @@ internal class CGMService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package no.nordicsemi.android.csc.repository
|
||||
|
||||
import android.util.Log
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.csc.data.CSCRepository
|
||||
import no.nordicsemi.android.csc.data.DisconnectCommand
|
||||
import no.nordicsemi.android.csc.data.SetWheelSizeCommand
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
@@ -23,7 +23,11 @@ internal class CSCService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
@@ -4,7 +4,10 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.hrs.data.HRSRepository
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.BleServiceStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -19,7 +22,11 @@ internal class HRSService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
@@ -4,7 +4,10 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.hts.data.HTSRepository
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.BleServiceStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -19,7 +22,11 @@ internal class HTSService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
@@ -3,7 +3,8 @@ package no.nordicsemi.android.prx.data
|
||||
internal data class PRXData(
|
||||
val batteryLevel: Int = 0,
|
||||
val localAlarmLevel: AlarmLevel = AlarmLevel.NONE,
|
||||
val isRemoteAlarm: Boolean = false
|
||||
val isRemoteAlarm: Boolean = false,
|
||||
val linkLossAlarmLevel: AlarmLevel = AlarmLevel.HIGH
|
||||
)
|
||||
|
||||
internal enum class AlarmLevel(val value: Int) {
|
||||
|
||||
@@ -27,16 +27,22 @@ internal class PRXRepository @Inject constructor() {
|
||||
_data.tryEmit(_data.value.copy(localAlarmLevel = alarmLevel))
|
||||
}
|
||||
|
||||
fun setLocalAlarmLevel(alarmLevel: AlarmLevel) {
|
||||
_data.tryEmit(_data.value.copy(localAlarmLevel = alarmLevel))
|
||||
}
|
||||
|
||||
fun setLinkLossLevel(value: Int) {
|
||||
val alarmLevel = AlarmLevel.create(value)
|
||||
_data.tryEmit(_data.value.copy(linkLossAlarmLevel = alarmLevel))
|
||||
}
|
||||
|
||||
fun setRemoteAlarmLevel(isOn: Boolean) {
|
||||
_data.tryEmit(_data.value.copy(isRemoteAlarm = isOn))
|
||||
}
|
||||
|
||||
fun invokeCommand(command: PRXCommand) {
|
||||
if (_command.subscriptionCount.value > 0) {
|
||||
_command.tryEmit(command)
|
||||
} else {
|
||||
_status.tryEmit(BleManagerStatus.DISCONNECTED)
|
||||
}
|
||||
_command.tryEmit(command)
|
||||
_status.tryEmit(BleManagerStatus.DISCONNECTED)
|
||||
}
|
||||
|
||||
fun setNewStatus(status: BleManagerStatus) {
|
||||
|
||||
@@ -7,10 +7,12 @@ import android.media.RingtoneManager
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import no.nordicsemi.android.prx.data.AlarmLevel
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
import javax.inject.Inject
|
||||
|
||||
class AlarmHandler @Inject constructor(
|
||||
internal class AlarmHandler @Inject constructor(
|
||||
@ApplicationContext
|
||||
private val context: Context
|
||||
) {
|
||||
@@ -34,28 +36,39 @@ class AlarmHandler @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun playAlarm() {
|
||||
fun playAlarm(alarmLevel: AlarmLevel) {
|
||||
val am = context.getSystemService(LifecycleService.AUDIO_SERVICE) as AudioManager
|
||||
originalVolume = am.getStreamVolume(AudioManager.STREAM_ALARM)
|
||||
|
||||
val soundLevel = when (alarmLevel) {
|
||||
AlarmLevel.NONE -> 0
|
||||
AlarmLevel.MEDIUM -> originalVolume / 2
|
||||
AlarmLevel.HIGH -> originalVolume
|
||||
}
|
||||
|
||||
am.setStreamVolume(
|
||||
AudioManager.STREAM_ALARM,
|
||||
am.getStreamMaxVolume(AudioManager.STREAM_ALARM),
|
||||
soundLevel,
|
||||
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE
|
||||
)
|
||||
try {
|
||||
mediaPlayer.prepare()
|
||||
mediaPlayer.start()
|
||||
} catch (e: IOException) {
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Prepare Alarm failed: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun pauseAlarm() {
|
||||
if (mediaPlayer.isPlaying) {
|
||||
mediaPlayer.stop()
|
||||
// Restore original volume
|
||||
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am.setStreamVolume(AudioManager.STREAM_ALARM, originalVolume, 0)
|
||||
try {
|
||||
if (mediaPlayer.isPlaying) {
|
||||
mediaPlayer.stop()
|
||||
// Restore original volume
|
||||
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am.setStreamVolume(AudioManager.STREAM_ALARM, originalVolume, 0)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Prepare Alarm failed: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ internal class PRXManager(
|
||||
|
||||
// Server characteristics.
|
||||
private var localAlertLevelCharacteristic: BluetoothGattCharacteristic? = null
|
||||
private var linkLossServerCharacteristic: BluetoothGattCharacteristic? = null
|
||||
/**
|
||||
* Returns true if the alert has been enabled on the proximity tag, false otherwise.
|
||||
*/
|
||||
@@ -77,8 +78,16 @@ internal class PRXManager(
|
||||
dataHolder.setLocalAlarmLevel(level)
|
||||
}
|
||||
})
|
||||
|
||||
setWriteCallback(linkLossServerCharacteristic)
|
||||
.with(object : AlertLevelDataCallback() {
|
||||
override fun onAlertLevelChanged(device: BluetoothDevice, level: Int) {
|
||||
dataHolder.setLinkLossLevel(level)
|
||||
}
|
||||
})
|
||||
|
||||
// After connection, set the Link Loss behaviour on the tag.
|
||||
writeCharacteristic(linkLossCharacteristic, AlertLevelData.highAlert())
|
||||
writeCharacteristic(linkLossCharacteristic, AlertLevelData.highAlert(), BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
||||
.done { device: BluetoothDevice? ->
|
||||
log(
|
||||
Log.INFO,
|
||||
@@ -97,9 +106,11 @@ internal class PRXManager(
|
||||
override fun onServerReady(server: BluetoothGattServer) {
|
||||
val immediateAlertService = server.getService(PRX_SERVICE_UUID)
|
||||
if (immediateAlertService != null) {
|
||||
localAlertLevelCharacteristic = immediateAlertService.getCharacteristic(
|
||||
ALERT_LEVEL_CHARACTERISTIC_UUID
|
||||
)
|
||||
localAlertLevelCharacteristic = immediateAlertService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
|
||||
}
|
||||
val linkLossService = server.getService(LINK_LOSS_SERVICE_UUID)
|
||||
if (linkLossService != null) {
|
||||
linkLossServerCharacteristic = linkLossService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +119,7 @@ internal class PRXManager(
|
||||
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
|
||||
val llService = gatt.getService(LINK_LOSS_SERVICE_UUID)
|
||||
if (llService != null) {
|
||||
linkLossCharacteristic =
|
||||
llService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
|
||||
linkLossCharacteristic = llService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
|
||||
}
|
||||
return linkLossCharacteristic != null
|
||||
}
|
||||
@@ -130,6 +140,7 @@ internal class PRXManager(
|
||||
alertLevelCharacteristic = null
|
||||
linkLossCharacteristic = null
|
||||
localAlertLevelCharacteristic = null
|
||||
linkLossServerCharacteristic = null
|
||||
// Reset the alert flag
|
||||
isAlertEnabled = false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package no.nordicsemi.android.prx.repository
|
||||
|
||||
import android.util.Log
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -8,6 +9,8 @@ import no.nordicsemi.android.prx.data.DisableAlarm
|
||||
import no.nordicsemi.android.prx.data.Disconnect
|
||||
import no.nordicsemi.android.prx.data.EnableAlarm
|
||||
import no.nordicsemi.android.prx.data.PRXRepository
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.BleServiceStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
@@ -35,7 +38,20 @@ internal class PRXService : ForegroundBleService() {
|
||||
serverManager.open()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val bleStatus = when (it) {
|
||||
BleServiceStatus.CONNECTING -> BleManagerStatus.CONNECTING
|
||||
BleServiceStatus.OK -> BleManagerStatus.OK
|
||||
BleServiceStatus.DISCONNECTED -> {
|
||||
scope.close()
|
||||
BleManagerStatus.DISCONNECTED
|
||||
}
|
||||
BleServiceStatus.LINK_LOSS -> null
|
||||
}.exhaustive
|
||||
bleStatus?.let { repository.setNewStatus(it) }
|
||||
|
||||
if (BleServiceStatus.LINK_LOSS == it) {
|
||||
repository.setLocalAlarmLevel(repository.data.value.linkLossAlarmLevel)
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
@@ -48,13 +64,17 @@ internal class PRXService : ForegroundBleService() {
|
||||
|
||||
repository.data.onEach {
|
||||
if (it.localAlarmLevel != AlarmLevel.NONE) {
|
||||
alarmHandler.playAlarm()
|
||||
alarmHandler.playAlarm(it.localAlarmLevel)
|
||||
} else {
|
||||
alarmHandler.pauseAlarm()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
}
|
||||
|
||||
override fun shouldAutoConnect(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
alarmHandler.releaseAlarm()
|
||||
|
||||
@@ -4,7 +4,10 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.rscs.data.RSCSRepository
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.BleServiceStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -19,7 +22,11 @@ internal class RSCSService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
@@ -3,6 +3,7 @@ package no.nordicsemi.android.uart.repository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.service.BleManagerStatus
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.uart.data.DisconnectCommand
|
||||
import no.nordicsemi.android.uart.data.SendTextCommand
|
||||
@@ -22,7 +23,11 @@ internal class UARTService : ForegroundBleService() {
|
||||
super.onCreate()
|
||||
|
||||
status.onEach {
|
||||
repository.setNewStatus(it)
|
||||
val status = it.mapToSimpleManagerStatus()
|
||||
repository.setNewStatus(status)
|
||||
if (status == BleManagerStatus.DISCONNECTED) {
|
||||
scope.close()
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
repository.command.onEach {
|
||||
|
||||
Reference in New Issue
Block a user