From 80fb980c385b3866606ec9789fcc7926ac1fa012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Mon, 31 Jan 2022 14:54:57 +0100 Subject: [PATCH] Fix glucose details screen --- .../android/gls/details/view/Field.kt | 38 ++- .../gls/details/view/GLSDetailsContentView.kt | 282 +++++++++++------- .../gls/details/view/GLSDetailsMappers.kt | 9 - .../details/viewmodel/GLSDetailsViewModel.kt | 4 +- .../android/gls/main/view/GLSContentView.kt | 46 ++- profile_gls/src/main/res/values/strings.xml | 2 +- settings.gradle | 2 +- 7 files changed, 251 insertions(+), 132 deletions(-) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt index 7d3262bb..59881bfe 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt @@ -7,6 +7,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import no.nordicsemi.android.gls.R @Composable internal fun Field(title: String, value: String) { @@ -16,11 +20,41 @@ internal fun Field(title: String, value: String) { ) { Text( text = title, - style = MaterialTheme.typography.titleMedium + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.outline ) Text( text = value, - style = MaterialTheme.typography.bodyMedium + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.End ) } } + +@Composable +internal fun BooleanField(title: String, value: Boolean) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.outline + ) + + if (value) { + Text( + text = stringResource(id = R.string.gls_yes), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.error + ) + } else { + Text( + text = stringResource(id = R.string.gls_no), + style = MaterialTheme.typography.bodyMedium, + color = colorResource(id = no.nordicsemi.android.material.you.R.color.nordicGrass) + ) + } + } +} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt index c8246efc..36d8f6de 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt @@ -1,119 +1,197 @@ package no.nordicsemi.android.gls.details.view -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Divider +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.unit.dp -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementCallback -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback import no.nordicsemi.android.gls.R -import no.nordicsemi.android.gls.data.* +import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.gls.main.view.toDisplayString -import java.util.* @Composable internal fun GLSDetailsContentView(record: GLSRecord) { - Field(stringResource(id = R.string.gls_details_sequence_number), record.sequenceNumber.toString()) + Column(modifier = Modifier.verticalScroll(rememberScrollState())) { + Column(modifier = Modifier.padding(16.dp)) { + Field( + stringResource(id = R.string.gls_details_sequence_number), + record.sequenceNumber.toString() + ) - record.time?.let { - Field(stringResource(id = R.string.gls_details_date_and_time), stringResource(R.string.gls_timestamp, it)) - Spacer(modifier = Modifier.size(4.dp)) - } - record.type?.let { - Field(stringResource(id = R.string.gls_details_type), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) - } - record.sampleLocation?.let { - Field(stringResource(id = R.string.gls_details_location), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) - } + record.time?.let { + Field( + stringResource(id = R.string.gls_details_date_and_time), + stringResource(R.string.gls_timestamp, it) + ) + } - Field(stringResource(id = R.string.gls_details_glucose_condensation_title), stringResource(id = R.string.gls_details_glucose_condensation_field, record.glucoseConcentration, record.unit.toDisplayString())) + Divider( + color = MaterialTheme.colorScheme.secondary, + thickness = 1.dp, + modifier = Modifier.padding(vertical = 16.dp) + ) - record.status?.let { - Field(stringResource(id = R.string.gls_details_battery_low), it.deviceBatteryLow.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_sensor_malfunction), it.sensorMalfunction.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_insufficient_sample), it.sampleSizeInsufficient.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_strip_insertion_error), it.stripInsertionError.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_strip_type_incorrect), it.stripTypeIncorrect.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_sensor_result_too_high), it.sensorResultHigherThenDeviceCanProcess.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_sensor_result_too_low), it.sensorResultLowerThenDeviceCanProcess.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_temperature_too_high), it.sensorTemperatureTooHigh.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_temperature_too_low), it.sensorTemperatureTooLow.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_strip_pulled_too_soon), it.sensorReadInterrupted.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_general_device_fault), it.generalDeviceFault.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_details_time_fault), it.timeFault.toGLSStatus()) - Spacer(modifier = Modifier.size(4.dp)) - } + record.type?.let { + Field(stringResource(id = R.string.gls_details_type), it.toDisplayString()) + Spacer(modifier = Modifier.size(4.dp)) + } - record.context?.let { - Field(stringResource(id = R.string.gls_context_title), stringResource(id = R.string.gls_available)) - Spacer(modifier = Modifier.size(4.dp)) - it.carbohydrate?.let { - Field(stringResource(id = R.string.gls_context_carbohydrate), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) + record.sampleLocation?.let { + Field(stringResource(id = R.string.gls_details_location), it.toDisplayString()) + Spacer(modifier = Modifier.size(4.dp)) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Bottom + ) { + Text( + text = stringResource(id = R.string.gls_details_glucose_condensation_title), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.outline + ) + Text( + text = stringResource( + id = R.string.gls_details_glucose_condensation_field, + record.glucoseConcentration, + record.unit.toDisplayString() + ), + style = MaterialTheme.typography.titleLarge + ) + } + + Divider( + color = MaterialTheme.colorScheme.secondary, + thickness = 1.dp, + modifier = Modifier.padding(vertical = 16.dp) + ) + + record.status?.let { + BooleanField( + stringResource(id = R.string.gls_details_battery_low), + it.deviceBatteryLow + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_sensor_malfunction), + it.sensorMalfunction + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_insufficient_sample), + it.sampleSizeInsufficient + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_strip_insertion_error), + it.stripInsertionError + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_strip_type_incorrect), + it.stripTypeIncorrect + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_sensor_result_too_high), + it.sensorResultHigherThenDeviceCanProcess + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_sensor_result_too_low), + it.sensorResultLowerThenDeviceCanProcess + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_temperature_too_high), + it.sensorTemperatureTooHigh + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_temperature_too_low), + it.sensorTemperatureTooLow + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_strip_pulled_too_soon), + it.sensorReadInterrupted + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField( + stringResource(id = R.string.gls_details_general_device_fault), + it.generalDeviceFault + ) + Spacer(modifier = Modifier.size(4.dp)) + BooleanField(stringResource(id = R.string.gls_details_time_fault), it.timeFault) + Spacer(modifier = Modifier.size(4.dp)) + } + + Divider( + color = MaterialTheme.colorScheme.secondary, + thickness = 1.dp, + modifier = Modifier.padding(vertical = 16.dp) + ) + + record.context?.let { + Field( + stringResource(id = R.string.gls_context_title), + stringResource(id = R.string.gls_available) + ) + Spacer(modifier = Modifier.size(4.dp)) + it.carbohydrate?.let { + Field( + stringResource(id = R.string.gls_context_carbohydrate), + it.toDisplayString() + ) + Spacer(modifier = Modifier.size(4.dp)) + } + it.meal?.let { + Field(stringResource(id = R.string.gls_context_meal), it.toDisplayString()) + Spacer(modifier = Modifier.size(4.dp)) + } + it.tester?.let { + Field(stringResource(id = R.string.gls_context_tester), it.toDisplayString()) + Spacer(modifier = Modifier.size(4.dp)) + } + it.health?.let { + Field(stringResource(id = R.string.gls_context_health), it.toDisplayString()) + Spacer(modifier = Modifier.size(4.dp)) + } + Field( + stringResource(id = R.string.gls_context_exercise_title), + stringResource( + id = R.string.gls_context_exercise_field, + it.exerciseDuration, + it.exerciseIntensity + ) + ) + Spacer(modifier = Modifier.size(4.dp)) + + val medicationField = String.format( + stringResource(id = R.string.gls_context_medication_field), + it.medicationQuantity, + it.medicationUnit.toDisplayString(), + it.medication?.toDisplayString() + ) + Field(stringResource(id = R.string.gls_context_medication_title), medicationField) + + Spacer(modifier = Modifier.size(4.dp)) + Field( + stringResource(id = R.string.gls_context_hba1c_title), + stringResource(id = R.string.gls_context_hba1c_field, it.HbA1c) + ) + Spacer(modifier = Modifier.size(4.dp)) + } ?: Field( + stringResource(id = R.string.gls_context_title), + stringResource(id = R.string.gls_unavailable) + ) } - it.meal?.let { - Field(stringResource(id = R.string.gls_context_meal), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) - } - it.tester?.let { - Field(stringResource(id = R.string.gls_context_tester), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) - } - it.health?.let { - Field(stringResource(id = R.string.gls_context_health), it.toDisplayString()) - Spacer(modifier = Modifier.size(4.dp)) - } - Field(stringResource(id = R.string.gls_context_exercise_title), stringResource(id = R.string.gls_context_exercise_field, it.exerciseDuration, it.exerciseIntensity)) - Spacer(modifier = Modifier.size(4.dp)) - - val medicationField = String.format(stringResource(id = R.string.gls_context_medication_field), it.medicationQuantity, it.medicationUnit.toDisplayString(), it.medication?.toDisplayString()) - Field(stringResource(id = R.string.gls_context_medication_title), medicationField) - - Spacer(modifier = Modifier.size(4.dp)) - Field(stringResource(id = R.string.gls_context_hba1c_title), stringResource(id = R.string.gls_context_hba1c_field, it.HbA1c)) - Spacer(modifier = Modifier.size(4.dp)) - } ?: Field(stringResource(id = R.string.gls_context_title), stringResource(id = R.string.gls_unavailable)) -} - -@Composable -private fun GLSDetailsContentView() { - val record = GLSRecord( - sequenceNumber = 1, - time = Calendar.getInstance(), - glucoseConcentration = 12f, - type = RecordType.ARTERIAL_PLASMA, - status = GlucoseMeasurementCallback.GlucoseStatus(0x0004), - unit = ConcentrationUnit.UNIT_KGPL, - sampleLocation = SampleLocation.FINGER, - context = MeasurementContext( - sequenceNumber = 3, - carbohydrate = GlucoseMeasurementContextCallback.Carbohydrate.BREAKFAST, - carbohydrateAmount = 23f, - meal = GlucoseMeasurementContextCallback.Meal.BEDTIME, - tester = GlucoseMeasurementContextCallback.Tester.HEALTH_CARE_PROFESSIONAL, - health = GlucoseMeasurementContextCallback.Health.MAJOR_HEALTH_ISSUES, - exerciseDuration = 3, - exerciseIntensity = 3, - medication = GlucoseMeasurementContextCallback.Medication.INTERMEDIATE_ACTING_INSULIN, - medicationQuantity = 4f, - medicationUnit = MedicationUnit.UNIT_KG, - HbA1c = 21f - ) - ) - GLSDetailsContentView(record) + } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt index 5199c3e3..48434847 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt @@ -100,12 +100,3 @@ internal fun Meal.toDisplayString(): String { Meal.BEDTIME -> stringResource(id = R.string.gls_meal_bedtime) } } - -@Composable -internal fun Boolean.toGLSStatus(): String { - return if (this) { - stringResource(id = R.string.gls_yes) - } else { - stringResource(id = R.string.gls_no) - } -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt index 0538dd84..0ed1212d 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt @@ -2,10 +2,10 @@ package no.nordicsemi.android.gls.details.viewmodel import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import no.nordicsemi.android.gls.GlsDetailsDestinationId import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.navigation.AnyArgument import no.nordicsemi.android.navigation.NavigationManager -import no.nordicsemi.ui.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel @@ -14,7 +14,7 @@ internal class GLSDetailsViewModel @Inject constructor( ) : ViewModel() { val record = - (navigationManager.getImmediateArgument(ScannerDestinationId) as AnyArgument).value as GLSRecord + (navigationManager.getImmediateArgument(GlsDetailsDestinationId) as AnyArgument).value as GLSRecord fun navigateBack() { navigationManager.navigateUp() diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt index 1ebb89a7..080d042e 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt @@ -2,6 +2,7 @@ package no.nordicsemi.android.gls.main.view import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Settings @@ -11,6 +12,7 @@ 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.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -103,7 +105,7 @@ private fun RecordsViewWithData(state: GLSData) { state.records.forEachIndexed { i, it -> RecordItem(it) - if (i < state.records.size-1) { + if (i < state.records.size - 1) { Spacer(modifier = Modifier.size(8.dp)) } } @@ -114,32 +116,46 @@ private fun RecordsViewWithData(state: GLSData) { private fun RecordItem(record: GLSRecord) { val viewModel: GLSViewModel = hiltViewModel() - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(10.dp)) + .clickable { viewModel.onEvent(OnGLSRecordClick(record)) } + .padding(8.dp) + ) { Column( modifier = Modifier .fillMaxWidth() .weight(1f) - .clickable { viewModel.onEvent(OnGLSRecordClick(record)) } ) { record.time?.let { Text( text = stringResource(R.string.gls_timestamp, it), - style = MaterialTheme.typography.labelLarge + style = MaterialTheme.typography.titleMedium ) } - Text( - text = record.type.toDisplayString(), - style = MaterialTheme.typography.bodyMedium - ) + Spacer(modifier = Modifier.size(4.dp)) + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = record.type.toDisplayString(), + style = MaterialTheme.typography.bodySmall + ) + + Text( + text = glucoseConcentrationDisplayValue( + record.glucoseConcentration, + record.unit + ), + style = MaterialTheme.typography.labelLarge, + ) + } } - - Spacer(modifier = Modifier.size(16.dp)) - - Text( - text = glucoseConcentrationDisplayValue(record.glucoseConcentration, record.unit), - style = MaterialTheme.typography.titleMedium, - ) } } diff --git a/profile_gls/src/main/res/values/strings.xml b/profile_gls/src/main/res/values/strings.xml index a517ca6c..8fc8cc70 100644 --- a/profile_gls/src/main/res/values/strings.xml +++ b/profile_gls/src/main/res/values/strings.xml @@ -103,7 +103,7 @@ Exercise: %d min %d%% Medication: - %d %s \n %s + %.2f%s\n%s HbA1c: %.2f%% diff --git a/settings.gradle b/settings.gradle index 399ce200..bdd5507b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,7 +33,7 @@ dependencyResolutionManagement { version('compose', '1.0.5') alias('compose-livedata').to('androidx.compose.runtime', 'runtime-livedata').versionRef('compose') alias('compose-ui').to('androidx.compose.ui', 'ui').versionRef('compose') - alias('compose-material').to('androidx.compose.material3:material3:1.0.0-alpha01') + alias('compose-material').to('androidx.compose.material3:material3:1.0.0-alpha04') alias('compose-tooling-preview').to('androidx.compose.ui', 'ui-tooling-preview').versionRef('compose') alias('compose-navigation').to('androidx.navigation:navigation-compose:2.4.0-alpha09') bundle('compose', ['compose-livedata', 'compose-ui', 'compose-material', 'compose-tooling-preview', 'compose-navigation'])