diff --git a/app/build.gradle b/app/build.gradle
index f9e6f36e..02f66864 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -88,6 +88,7 @@ dependencies {
implementation libs.bundles.hilt
kapt libs.bundles.hiltkapt
+ implementation libs.bundles.icons
implementation libs.bundles.compose
implementation libs.androidx.core
implementation libs.material
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt
index a4347e6d..fae7779a 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt
@@ -8,6 +8,7 @@ import no.nordicsemi.android.hrs.view.HRSScreen
import no.nordicsemi.android.hts.view.HTSScreen
import no.nordicsemi.android.navigation.ComposeDestination
import no.nordicsemi.android.navigation.ComposeDestinations
+import no.nordicsemi.android.nrftoolbox.view.HomeScreen
import no.nordicsemi.android.prx.view.PRXScreen
import no.nordicsemi.android.rscs.view.RSCSScreen
import no.nordicsemi.android.uart.view.UARTScreen
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeViewModel.kt
deleted file mode 100644
index 4bfcd046..00000000
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeViewModel.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package no.nordicsemi.android.nrftoolbox
-
-import androidx.lifecycle.ViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import no.nordicsemi.android.navigation.NavigationManager
-import javax.inject.Inject
-
-@HiltViewModel
-class HomeViewModel @Inject constructor(
- private val navigationManager: NavigationManager
-) : ViewModel() {
-
- fun openProfile(destination: ProfileDestination) {
- navigationManager.navigateTo(destination.destination.id)
- }
-}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt
similarity index 67%
rename from app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt
rename to app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt
index 989d598d..811699e2 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt
@@ -1,4 +1,4 @@
-package no.nordicsemi.android.nrftoolbox
+package no.nordicsemi.android.nrftoolbox.view
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
@@ -6,19 +6,25 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.PlayArrow
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import no.nordicsemi.android.nrftoolbox.R
import no.nordicsemi.android.theme.view.ScreenSection
@Composable
@@ -26,6 +32,7 @@ fun FeatureButton(
@DrawableRes iconId: Int,
@StringRes nameCode: Int,
@StringRes name: Int,
+ isRunning: Boolean? = null,
onClick: () -> Unit
) {
ScreenSection(onClick = onClick) {
@@ -57,15 +64,32 @@ fun FeatureButton(
Spacer(modifier = Modifier.size(16.dp))
- Text(
- text = stringResource(id = nameCode),
- style = MaterialTheme.typography.headlineSmall,
- textAlign = TextAlign.Center
- )
+// Text(
+// text = stringResource(id = nameCode),
+// style = MaterialTheme.typography.headlineSmall,
+// textAlign = TextAlign.Center
+// )
+
+ isRunning?.let {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_running_indicator),
+ contentDescription = stringResource(id = R.string.running_profile_icon),
+ tint = getRunningIndicatorColor(it)
+ )
+ }
}
}
}
+@Composable
+private fun getRunningIndicatorColor(isRunning: Boolean): Color {
+ return if (isRunning) {
+ colorResource(id = R.color.nordicGrass)
+ } else {
+ MaterialTheme.colorScheme.outline
+ }
+}
+
@Preview
@Composable
private fun FeatureButtonPreview() {
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeView.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt
similarity index 79%
rename from app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeView.kt
rename to app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt
index cd32f050..4eebebee 100644
--- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeView.kt
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt
@@ -1,4 +1,4 @@
-package no.nordicsemi.android.nrftoolbox
+package no.nordicsemi.android.nrftoolbox.view
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -6,17 +6,23 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
+import no.nordicsemi.android.nrftoolbox.BuildConfig
+import no.nordicsemi.android.nrftoolbox.ProfileDestination
+import no.nordicsemi.android.nrftoolbox.R
+import no.nordicsemi.android.nrftoolbox.viewmodel.HomeViewModel
import no.nordicsemi.android.theme.view.TitleAppBar
@Composable
fun HomeScreen() {
val viewModel: HomeViewModel = hiltViewModel()
+ val state = viewModel.state.collectAsState().value
Column {
TitleAppBar(stringResource(id = R.string.app_name))
@@ -34,21 +40,15 @@ fun HomeScreen() {
Spacer(modifier = Modifier.height(16.dp))
Text(
- text = stringResource(id = R.string.bluetooth_services),
+ text = stringResource(id = R.string.viewmodel_profiles),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(16.dp))
- FeatureButton(R.drawable.ic_csc, R.string.csc_module, R.string.csc_module_full) {
- viewModel.openProfile(ProfileDestination.CSC)
- }
-
- Spacer(modifier = Modifier.height(16.dp))
-
- FeatureButton(R.drawable.ic_hrs, R.string.hrs_module, R.string.hrs_module_full) {
- viewModel.openProfile(ProfileDestination.HRS)
+ FeatureButton(R.drawable.ic_bps, R.string.bps_module, R.string.bps_module_full) {
+ viewModel.openProfile(ProfileDestination.BPS)
}
Spacer(modifier = Modifier.height(16.dp))
@@ -59,31 +59,45 @@ fun HomeScreen() {
Spacer(modifier = Modifier.height(16.dp))
- FeatureButton(R.drawable.ic_hts, R.string.hts_module, R.string.hts_module_full) {
+ Text(
+ text = stringResource(id = R.string.service_profiles),
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ FeatureButton(R.drawable.ic_csc, R.string.csc_module, R.string.csc_module_full, state.isCSCModuleRunning) {
+ viewModel.openProfile(ProfileDestination.CSC)
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ FeatureButton(R.drawable.ic_hrs, R.string.hrs_module, R.string.hrs_module_full, state.isHRSModuleRunning) {
+ viewModel.openProfile(ProfileDestination.HRS)
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ FeatureButton(R.drawable.ic_hts, R.string.hts_module, R.string.hts_module_full, state.isHTSModuleRunning) {
viewModel.openProfile(ProfileDestination.HTS)
}
Spacer(modifier = Modifier.height(16.dp))
- FeatureButton(R.drawable.ic_bps, R.string.bps_module, R.string.bps_module_full) {
- viewModel.openProfile(ProfileDestination.BPS)
- }
-
- Spacer(modifier = Modifier.height(16.dp))
-
- FeatureButton(R.drawable.ic_rscs, R.string.rscs_module, R.string.rscs_module_full) {
+ FeatureButton(R.drawable.ic_rscs, R.string.rscs_module, R.string.rscs_module_full, state.isRSCSModuleRunning) {
viewModel.openProfile(ProfileDestination.RSCS)
}
Spacer(modifier = Modifier.height(16.dp))
- FeatureButton(R.drawable.ic_prx, R.string.prx_module, R.string.prx_module_full) {
+ FeatureButton(R.drawable.ic_prx, R.string.prx_module, R.string.prx_module_full, state.isPRXModuleRunning) {
viewModel.openProfile(ProfileDestination.PRX)
}
Spacer(modifier = Modifier.height(16.dp))
- FeatureButton(R.drawable.ic_cgm, R.string.cgm_module, R.string.cgm_module_full) {
+ FeatureButton(R.drawable.ic_cgm, R.string.cgm_module, R.string.cgm_module_full, state.isCGMModuleRunning) {
viewModel.openProfile(ProfileDestination.CGMS)
}
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeViewState.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeViewState.kt
new file mode 100644
index 00000000..d8b47492
--- /dev/null
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeViewState.kt
@@ -0,0 +1,10 @@
+package no.nordicsemi.android.nrftoolbox.view
+
+data class HomeViewState(
+ val isCSCModuleRunning: Boolean = false,
+ val isHRSModuleRunning: Boolean = false,
+ val isHTSModuleRunning: Boolean = false,
+ val isRSCSModuleRunning: Boolean = false,
+ val isPRXModuleRunning: Boolean = false,
+ val isCGMModuleRunning: Boolean = false
+)
diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt
new file mode 100644
index 00000000..3aaabb4a
--- /dev/null
+++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt
@@ -0,0 +1,34 @@
+package no.nordicsemi.android.nrftoolbox.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import no.nordicsemi.android.cgms.data.CGMRepository
+import no.nordicsemi.android.navigation.NavigationManager
+import no.nordicsemi.android.nrftoolbox.ProfileDestination
+import no.nordicsemi.android.nrftoolbox.view.HomeViewState
+import javax.inject.Inject
+
+@HiltViewModel
+class HomeViewModel @Inject constructor(
+ private val navigationManager: NavigationManager,
+ private val cgmRepository: CGMRepository
+) : ViewModel() {
+
+ private val _state = MutableStateFlow(HomeViewState())
+ val state = _state.asStateFlow()
+
+ init {
+ cgmRepository.isRunning.onEach {
+ _state.value = _state.value.copy(isCGMModuleRunning = it)
+ }.launchIn(viewModelScope)
+ }
+
+ fun openProfile(destination: ProfileDestination) {
+ navigationManager.navigateTo(destination.destination.id)
+ }
+}
diff --git a/app/src/main/res/drawable/ic_running_indicator.xml b/app/src/main/res/drawable/ic_running_indicator.xml
new file mode 100644
index 00000000..9aa87408
--- /dev/null
+++ b/app/src/main/res/drawable/ic_running_indicator.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3b55da1f..b531a5a2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -20,6 +20,9 @@
DFU
Available in separate application.
- Bluetooth services
+ ViewModel profiles
+ Service profiles
Utils services
+
+ Icon indicating if the profile is running
diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceConnectingView.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceConnectingView.kt
index 03a2e645..c83e2a98 100644
--- a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceConnectingView.kt
+++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceConnectingView.kt
@@ -9,10 +9,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.HourglassTop
+import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@@ -22,11 +24,12 @@ import no.nordicsemi.android.theme.R
import no.nordicsemi.android.theme.view.ScreenSection
@Composable
-fun DeviceConnectingView() {
+fun DeviceConnectingView(navigateUp: () -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
- .padding(16.dp)
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
) {
ScreenSection {
Icon(
@@ -64,11 +67,17 @@ fun DeviceConnectingView() {
style = MaterialTheme.typography.titleLarge
)
}
+
+ Spacer(modifier = Modifier.size(16.dp))
+
+ Button(onClick = { navigateUp() }) {
+ Text(text = stringResource(id = R.string.disconnect))
+ }
}
}
@Preview
@Composable
fun DeviceConnectingView_Preview() {
- DeviceConnectingView()
+ DeviceConnectingView { }
}
diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceDisconnectedView.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceDisconnectedView.kt
index aef83bc1..577aac1c 100644
--- a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceDisconnectedView.kt
+++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/DeviceDisconnectedView.kt
@@ -5,10 +5,12 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.HighlightOff
+import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@@ -22,11 +24,12 @@ enum class Reason {
}
@Composable
-fun DeviceDisconnectedView(reason: Reason) {
+fun DeviceDisconnectedView(reason: Reason, navigateUp: () -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
- .padding(16.dp)
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
) {
ScreenSection {
Icon(
@@ -62,11 +65,17 @@ fun DeviceDisconnectedView(reason: Reason) {
style = MaterialTheme.typography.bodyMedium
)
}
+
+ Spacer(modifier = Modifier.size(16.dp))
+
+ Button(onClick = { navigateUp() }) {
+ Text(text = stringResource(id = R.string.go_up))
+ }
}
}
@Preview
@Composable
fun DeviceDisconnectedView_Preview() {
- DeviceConnectingView()
+ DeviceConnectingView { }
}
diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/NoDeviceView.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/NoDeviceView.kt
index cad618cd..cd027073 100644
--- a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/NoDeviceView.kt
+++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/scanner/NoDeviceView.kt
@@ -70,5 +70,5 @@ fun NoDeviceView() {
@Preview
@Composable
fun NoDeviceView_Preview() {
- DeviceConnectingView()
+ DeviceConnectingView { }
}
diff --git a/lib_theme/src/main/res/values/strings.xml b/lib_theme/src/main/res/values/strings.xml
index a9b27fe4..cee533f9 100644
--- a/lib_theme/src/main/res/values/strings.xml
+++ b/lib_theme/src/main/res/values/strings.xml
@@ -5,10 +5,12 @@
Dialog
CANCEL
+ Go up
+
Close the application.
Close the current screen.
- DISCONNECT
+ Disconnect
Battery
Disconnected
diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt
index 63400a73..850b75ea 100644
--- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt
+++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt
@@ -24,6 +24,8 @@ fun BPSScreen() {
val state = viewModel.state.collectAsState().value
Column {
+ val navigateUp = { viewModel.onEvent(DisconnectEvent) }
+
BackIconAppBar(stringResource(id = R.string.bps_title)) {
viewModel.onEvent(DisconnectEvent)
}
@@ -32,11 +34,11 @@ fun BPSScreen() {
when (state) {
NoDeviceState -> NoDeviceView()
is WorkingState -> when (state.result) {
- is ConnectingResult -> DeviceConnectingView()
- is DisconnectedResult -> DeviceDisconnectedView(Reason.USER)
- is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS)
- is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE)
- is ReadyResult -> DeviceConnectingView()
+ is ConnectingResult,
+ is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
+ is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
+ is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
+ is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
is SuccessResult -> BPSContentView(state.result.data) { viewModel.onEvent(it) }
}
}.exhaustive
diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRepository.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRepository.kt
index bf20a1c6..bed4fef6 100644
--- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRepository.kt
+++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRepository.kt
@@ -2,24 +2,25 @@ package no.nordicsemi.android.cgms.data
import android.bluetooth.BluetoothDevice
import android.content.Context
-import android.util.Log
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.cgms.repository.CGMManager
import no.nordicsemi.android.cgms.repository.CGMService
import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.ConnectingResult
import no.nordicsemi.android.service.ServiceManager
-import no.nordicsemi.android.utils.exhaustive
+import java.lang.Exception
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-internal class CGMRepository @Inject constructor(
+class CGMRepository @Inject constructor(
@ApplicationContext
private val context: Context,
private val serviceManager: ServiceManager,
@@ -27,38 +28,55 @@ internal class CGMRepository @Inject constructor(
private var manager: CGMManager? = null
private val _data = MutableStateFlow>(ConnectingResult())
- val data = _data.asStateFlow()
+ internal val data = _data.asStateFlow()
+
+ private val _isRunning = MutableStateFlow(false)
+ val isRunning = _isRunning.asStateFlow()
fun launch(device: BluetoothDevice) {
serviceManager.startService(CGMService::class.java, device)
}
- fun startManager(device: BluetoothDevice, scope: CoroutineScope) {
+ fun start(device: BluetoothDevice, scope: CoroutineScope) {
val manager = CGMManager(context, scope)
manager.dataHolder.status.onEach {
_data.value = it
- Log.d("AAATESTAAA", "data: $it")
}.launchIn(scope)
- manager.connect(device)
- .useAutoConnect(false)
- .retry(3, 100)
- .enqueue()
+ scope.launch {
+ manager.start(device)
+ }
}
- fun sendNewServiceCommand(workingMode: CGMServiceCommand) {
- when (workingMode) {
- CGMServiceCommand.REQUEST_ALL_RECORDS -> manager?.requestAllRecords()
- CGMServiceCommand.REQUEST_LAST_RECORD -> manager?.requestLastRecord()
- CGMServiceCommand.REQUEST_FIRST_RECORD -> manager?.requestFirstRecord()
- CGMServiceCommand.DISCONNECT -> release()
- }.exhaustive
+ private suspend fun CGMManager.start(device: BluetoothDevice) {
+ try {
+ connect(device)
+ .useAutoConnect(false)
+ .retry(3, 100)
+ .suspend()
+ _isRunning.value = true
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
}
- private fun release() {
+ fun requestAllRecords() {
+ manager?.requestAllRecords()
+ }
+
+ fun requestLastRecord() {
+ manager?.requestLastRecord()
+ }
+
+ fun requestFirstRecord() {
+ manager?.requestFirstRecord()
+ }
+
+ fun release() {
serviceManager.stopService(CGMService::class.java)
manager?.disconnect()?.enqueue()
manager = null
+ _isRunning.value = false
}
}
diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMManager.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMManager.kt
index c22818a3..50779e2f 100644
--- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMManager.kt
+++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMManager.kt
@@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointResponse
+import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse
import no.nordicsemi.android.ble.common.callback.cgm.CGMFeatureResponse
import no.nordicsemi.android.ble.common.callback.cgm.CGMSpecificOpsControlPointResponse
import no.nordicsemi.android.ble.common.callback.cgm.CGMStatusResponse
@@ -98,6 +99,11 @@ internal class CGMManager(
return CGMManagerGattCallback()
}
+ override fun log(priority: Int, message: String) {
+ super.log(priority, message)
+ Log.d("COROUTINE-EXCEPTION", message)
+ }
+
private inner class CGMManagerGattCallback : BleManagerGattCallback() {
override fun initialize() {
super.initialize()
@@ -169,6 +175,11 @@ internal class CGMManager(
}
}.launchIn(scope)
+ setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow()
+ .onEach {
+ data.value = data.value.copy(batteryLevel = it.batteryLevel)
+ }.launchIn(scope)
+
enableNotifications(cgmMeasurementCharacteristic).enqueue()
enableIndications(cgmSpecificOpsControlPointCharacteristic).enqueue()
enableIndications(recordAccessControlPointCharacteristic).enqueue()
@@ -191,7 +202,9 @@ internal class CGMManager(
val sequenceNumber = records.keyAt(records.size() - 1) + 1
writeCharacteristic(
recordAccessControlPointCharacteristic,
- RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber),
+ RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(
+ sequenceNumber
+ ),
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
).suspend()
} else {
@@ -232,28 +245,31 @@ internal class CGMManager(
}
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
- val service = gatt.getService(CGMS_SERVICE_UUID)
- if (service != null) {
- cgmStatusCharacteristic = service.getCharacteristic(CGM_STATUS_UUID)
- cgmFeatureCharacteristic = service.getCharacteristic(CGM_FEATURE_UUID)
- cgmMeasurementCharacteristic = service.getCharacteristic(CGM_MEASUREMENT_UUID)
- cgmSpecificOpsControlPointCharacteristic = service.getCharacteristic(
- CGM_OPS_CONTROL_POINT_UUID
- )
- recordAccessControlPointCharacteristic = service.getCharacteristic(RACP_UUID)
+ gatt.getService(CGMS_SERVICE_UUID)?.run {
+ cgmStatusCharacteristic = getCharacteristic(CGM_STATUS_UUID)
+ cgmFeatureCharacteristic = getCharacteristic(CGM_FEATURE_UUID)
+ cgmMeasurementCharacteristic = getCharacteristic(CGM_MEASUREMENT_UUID)
+ cgmSpecificOpsControlPointCharacteristic = getCharacteristic(CGM_OPS_CONTROL_POINT_UUID)
+ recordAccessControlPointCharacteristic = getCharacteristic(RACP_UUID)
}
- return cgmMeasurementCharacteristic != null && cgmSpecificOpsControlPointCharacteristic != null && recordAccessControlPointCharacteristic != null && cgmStatusCharacteristic != null && cgmFeatureCharacteristic != null
+ gatt.getService(BATTERY_SERVICE_UUID)?.run {
+ batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)
+ }
+ return batteryLevelCharacteristic != null
+ && cgmMeasurementCharacteristic != null
+ && cgmSpecificOpsControlPointCharacteristic != null
+ && recordAccessControlPointCharacteristic != null
+ && cgmStatusCharacteristic != null
+ && cgmFeatureCharacteristic != null
}
- override fun onServicesInvalidated() {}
-
- override fun onDeviceDisconnected() {
- super.onDeviceDisconnected()
+ override fun onServicesInvalidated() {
cgmStatusCharacteristic = null
cgmFeatureCharacteristic = null
cgmMeasurementCharacteristic = null
cgmSpecificOpsControlPointCharacteristic = null
recordAccessControlPointCharacteristic = null
+ batteryLevelCharacteristic = null
}
}
diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt
index 74eed617..73339fc2 100644
--- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt
+++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt
@@ -20,7 +20,7 @@ internal class CGMService : NotificationService() {
val device = intent!!.getParcelableExtra(DEVICE_DATA)!!
- repository.startManager(device, lifecycleScope)
+ repository.start(device, lifecycleScope)
return START_REDELIVER_INTENT
}
diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt
index ef18c059..048dd298 100644
--- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt
+++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt
@@ -24,19 +24,19 @@ fun CGMScreen() {
val state = viewModel.state.collectAsState().value
Column {
- BackIconAppBar(stringResource(id = R.string.cgms_title)) {
- viewModel.onEvent(DisconnectEvent)
- }
+ val navigateUp = { viewModel.onEvent(NavigateUp) }
+
+ BackIconAppBar(stringResource(id = R.string.cgms_title), navigateUp)
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
when (state) {
NoDeviceState -> NoDeviceView()
is WorkingState -> when (state.result) {
- is ConnectingResult -> DeviceConnectingView()
- is DisconnectedResult -> DeviceDisconnectedView(Reason.USER)
- is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS)
- is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE)
- is ReadyResult -> DeviceConnectingView()
+ is ConnectingResult,
+ is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
+ is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
+ is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
+ is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
is SuccessResult -> CGMContentView(state.result.data) { viewModel.onEvent(it) }
}
}.exhaustive
diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMScreenViewModel.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMScreenViewModel.kt
index cb9f3b7e..a5be007e 100644
--- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMScreenViewModel.kt
+++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMScreenViewModel.kt
@@ -1,6 +1,5 @@
package no.nordicsemi.android.cgms.viewmodel
-import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -29,6 +28,24 @@ internal class CGMScreenViewModel @Inject constructor(
val state = _state.asStateFlow()
init {
+ if (!repository.isRunning.value) {
+ requestBluetoothDevice()
+ }
+
+ repository.data.onEach {
+ _state.value = WorkingState(it)
+ }.launchIn(viewModelScope)
+ }
+
+ fun onEvent(event: CGMViewEvent) {
+ when (event) {
+ DisconnectEvent -> disconnect()
+ is OnWorkingModeSelected -> onCommandReceived(event.workingMode)
+ NavigateUp -> navigationManager.navigateUp()
+ }.exhaustive
+ }
+
+ private fun requestBluetoothDevice() {
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(CGMS_SERVICE_UUID))
navigationManager.recentResult.onEach {
@@ -36,11 +53,6 @@ internal class CGMScreenViewModel @Inject constructor(
handleArgs(it)
}
}.launchIn(viewModelScope)
-
- repository.data.onEach {
- _state.value = WorkingState(it)
- Log.d("AAATESTAAA", "vm data: $it")
- }.launchIn(viewModelScope)
}
private fun handleArgs(args: DestinationResult) {
@@ -50,11 +62,12 @@ internal class CGMScreenViewModel @Inject constructor(
}.exhaustive
}
- fun onEvent(event: CGMViewEvent) {
- when (event) {
- DisconnectEvent -> disconnect()
- is OnWorkingModeSelected -> repository.sendNewServiceCommand(event.workingMode)
- NavigateUp -> navigationManager.navigateUp()
+ private fun onCommandReceived(workingMode: CGMServiceCommand) {
+ when (workingMode) {
+ CGMServiceCommand.REQUEST_ALL_RECORDS -> repository.requestAllRecords()
+ CGMServiceCommand.REQUEST_LAST_RECORD -> repository.requestLastRecord()
+ CGMServiceCommand.REQUEST_FIRST_RECORD -> repository.requestFirstRecord()
+ CGMServiceCommand.DISCONNECT -> disconnect()
}.exhaustive
}
@@ -63,6 +76,7 @@ internal class CGMScreenViewModel @Inject constructor(
}
private fun disconnect() {
- repository.sendNewServiceCommand(CGMServiceCommand.DISCONNECT)
+ repository.release()
+ navigationManager.navigateUp()
}
}
diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt
index 7f9d1d3b..7871c368 100644
--- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt
+++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt
@@ -1,6 +1,5 @@
package no.nordicsemi.android.gls.main.view
-import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -25,21 +24,21 @@ fun GLSScreen() {
val state = viewModel.state.collectAsState().value
Column {
+ val navigateUp = { viewModel.onEvent(DisconnectEvent) }
+
BackIconAppBar(stringResource(id = R.string.gls_title)) {
viewModel.onEvent(DisconnectEvent)
}
- Log.d("AAATESTAAA", "state: $state")
-
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
when (state) {
NoDeviceState -> NoDeviceView()
is WorkingState -> when (state.result) {
- is ConnectingResult -> DeviceConnectingView()
- is DisconnectedResult -> DeviceDisconnectedView(Reason.USER)
- is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS)
- is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE)
- is ReadyResult -> DeviceConnectingView()
+ is ConnectingResult,
+ is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
+ is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
+ is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
+ is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
is SuccessResult -> GLSContentView(state.result.data) { viewModel.onEvent(it) }
}
}.exhaustive