mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-19 15:34:26 +01:00
Merge pull request #109 from NordicSemiconductor/feature/addSendTextToUartProfile
Feature/add send text to uart profile
This commit is contained in:
2
.github/workflows/deploy-to-play-store.yml
vendored
2
.github/workflows/deploy-to-play-store.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
SONATYPE_STATING_PROFILE_ID: ${{ secrets.SONATYPE_STATING_PROFILE_ID }}
|
SONATYPE_STATING_PROFILE_ID: ${{ secrets.SONATYPE_STATING_PROFILE_ID }}
|
||||||
run: |
|
run: |
|
||||||
pwd
|
pwd
|
||||||
echo "${{ secrets.GOOGLE_SERVICES }}" | base64 --decode > lib_analytics/src/main/res/values/keys.xml
|
echo "${{ secrets.GOOGLE_SERVICES }}" | base64 --decode > lib_analytics/src/main/res/values/values.xml
|
||||||
echo "${{ secrets.KEYSTORE_FILE }}" > keystore.asc
|
echo "${{ secrets.KEYSTORE_FILE }}" > keystore.asc
|
||||||
gpg -d --passphrase "${{ secrets.KEYSTORE_FILE_PSWD }}" --batch keystore.asc > keystore
|
gpg -d --passphrase "${{ secrets.KEYSTORE_FILE_PSWD }}" --batch keystore.asc > keystore
|
||||||
echo "${{ secrets.GPG_FILE }}" > sec.gpg.asc
|
echo "${{ secrets.GPG_FILE }}" > sec.gpg.asc
|
||||||
|
|||||||
@@ -9,3 +9,14 @@ dependencies {
|
|||||||
implementation 'com.google.firebase:firebase-analytics'
|
implementation 'com.google.firebase:firebase-analytics'
|
||||||
implementation 'com.google.firebase:firebase-crashlytics'
|
implementation 'com.google.firebase:firebase-crashlytics'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task copyGoogleIdValuesTask(type: Copy) {
|
||||||
|
from 'src/main/res/values/values.xml'
|
||||||
|
into "$project.buildDir/generated/res/google-services/release/values/"
|
||||||
|
}
|
||||||
|
|
||||||
|
import com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask
|
||||||
|
|
||||||
|
tasks.withType(UploadMappingFileTask).configureEach {
|
||||||
|
dependsOn(copyGoogleIdValuesTask)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,7 @@ package no.nordicsemi.android.theme.view
|
|||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -28,6 +23,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
fun SectionTitle(
|
fun SectionTitle(
|
||||||
@DrawableRes resId: Int,
|
@DrawableRes resId: Int,
|
||||||
title: String,
|
title: String,
|
||||||
|
menu: @Composable (() -> Unit)? = null,
|
||||||
modifier: Modifier = Modifier.fillMaxWidth()
|
modifier: Modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -49,10 +45,12 @@ fun SectionTitle(
|
|||||||
Spacer(modifier = Modifier.size(8.dp))
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Start,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
menu?.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,13 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -19,17 +22,22 @@ import no.nordicsemi.android.theme.view.BatteryLevelView
|
|||||||
import no.nordicsemi.android.theme.view.SectionTitle
|
import no.nordicsemi.android.theme.view.SectionTitle
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun HRSContentView(state: HRSData, onEvent: (HRSScreenViewEvent) -> Unit) {
|
internal fun HRSContentView(state: HRSData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
ScreenSection {
|
ScreenSection {
|
||||||
SectionTitle(resId = R.drawable.ic_chart_line, title = "Data")
|
SectionTitle(
|
||||||
|
resId = R.drawable.ic_chart_line,
|
||||||
|
title = stringResource(id = R.string.hrs_section_data),
|
||||||
|
menu = { Menu(zoomIn, onEvent) }
|
||||||
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
LineChartView(state)
|
LineChartView(state, zoomIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -48,8 +56,22 @@ internal fun HRSContentView(state: HRSData, onEvent: (HRSScreenViewEvent) -> Uni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Menu(zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) {
|
||||||
|
val icon = when (zoomIn) {
|
||||||
|
true -> R.drawable.ic_zoom_out
|
||||||
|
false -> R.drawable.ic_zoom_in
|
||||||
|
}
|
||||||
|
IconButton(onClick = { onEvent(SwitchZoomEvent) }) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = icon),
|
||||||
|
contentDescription = stringResource(id = R.string.hrs_zoom_icon)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun Preview() {
|
private fun Preview() {
|
||||||
HRSContentView(state = HRSData()) { }
|
HRSContentView(state = HRSData(), zoomIn = false) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import no.nordicsemi.android.hrs.R
|
import no.nordicsemi.android.hrs.R
|
||||||
import no.nordicsemi.android.hrs.viewmodel.HRSViewModel
|
import no.nordicsemi.android.hrs.viewmodel.HRSViewModel
|
||||||
import no.nordicsemi.android.service.*
|
import no.nordicsemi.android.service.*
|
||||||
import no.nordicsemi.android.theme.view.BackIconAppBar
|
|
||||||
import no.nordicsemi.android.theme.view.LoggerIconAppBar
|
import no.nordicsemi.android.theme.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.ui.scanner.ui.DeviceConnectingView
|
|
||||||
import no.nordicsemi.ui.scanner.ui.NoDeviceView
|
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.utils.exhaustive
|
||||||
|
import no.nordicsemi.ui.scanner.ui.DeviceConnectingView
|
||||||
import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView
|
import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView
|
||||||
|
import no.nordicsemi.ui.scanner.ui.NoDeviceView
|
||||||
import no.nordicsemi.ui.scanner.ui.Reason
|
import no.nordicsemi.ui.scanner.ui.Reason
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -40,7 +39,7 @@ fun HRSScreen() {
|
|||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
||||||
is SuccessResult -> HRSContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> HRSContentView(state.result.data, state.zoomIn) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package no.nordicsemi.android.hrs.view
|
|||||||
|
|
||||||
internal sealed class HRSScreenViewEvent
|
internal sealed class HRSScreenViewEvent
|
||||||
|
|
||||||
|
internal object SwitchZoomEvent : HRSScreenViewEvent()
|
||||||
|
|
||||||
internal object DisconnectEvent : HRSScreenViewEvent()
|
internal object DisconnectEvent : HRSScreenViewEvent()
|
||||||
|
|
||||||
internal object NavigateUpEvent : HRSScreenViewEvent()
|
internal object NavigateUpEvent : HRSScreenViewEvent()
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import no.nordicsemi.android.service.BleManagerResult
|
|||||||
|
|
||||||
internal sealed class HRSViewState
|
internal sealed class HRSViewState
|
||||||
|
|
||||||
internal data class WorkingState(val result: BleManagerResult<HRSData>) : HRSViewState()
|
internal data class WorkingState(
|
||||||
|
val result: BleManagerResult<HRSData>,
|
||||||
|
val zoomIn: Boolean = false,
|
||||||
|
) : HRSViewState()
|
||||||
|
|
||||||
internal object NoDeviceState : HRSViewState()
|
internal object NoDeviceState : HRSViewState()
|
||||||
|
|||||||
@@ -17,24 +17,31 @@ import com.github.mikephil.charting.data.LineData
|
|||||||
import com.github.mikephil.charting.data.LineDataSet
|
import com.github.mikephil.charting.data.LineDataSet
|
||||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
|
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
|
||||||
import no.nordicsemi.android.hrs.data.HRSData
|
import no.nordicsemi.android.hrs.data.HRSData
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
private const val X_AXIS_ELEMENTS_COUNT = 40f
|
private const val X_AXIS_ELEMENTS_COUNT = 40f
|
||||||
|
|
||||||
|
private const val AXIS_MIN = 0
|
||||||
|
private const val AXIS_MAX = 300
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LineChartView(state: HRSData) {
|
internal fun LineChartView(state: HRSData, zoomIn: Boolean,) {
|
||||||
val items = state.heartRates.takeLast(X_AXIS_ELEMENTS_COUNT.toInt()).reversed()
|
val items = state.heartRates.takeLast(X_AXIS_ELEMENTS_COUNT.toInt()).reversed()
|
||||||
val isSystemInDarkTheme = isSystemInDarkTheme()
|
val isSystemInDarkTheme = isSystemInDarkTheme()
|
||||||
AndroidView(
|
AndroidView(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(300.dp),
|
.height(300.dp),
|
||||||
factory = { createLineChartView(isSystemInDarkTheme, it, items) },
|
factory = { createLineChartView(isSystemInDarkTheme, it, items, zoomIn) },
|
||||||
update = { updateData(items, it) }
|
update = { updateData(items, it, zoomIn) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createLineChartView(isDarkTheme: Boolean, context: Context, points: List<Int>): LineChart {
|
internal fun createLineChartView(
|
||||||
|
isDarkTheme: Boolean,
|
||||||
|
context: Context,
|
||||||
|
points: List<Int>,
|
||||||
|
zoomIn: Boolean
|
||||||
|
): LineChart {
|
||||||
return LineChart(context).apply {
|
return LineChart(context).apply {
|
||||||
description.isEnabled = false
|
description.isEnabled = false
|
||||||
|
|
||||||
@@ -73,8 +80,8 @@ internal fun createLineChartView(isDarkTheme: Boolean, context: Context, points:
|
|||||||
axisLeft.apply {
|
axisLeft.apply {
|
||||||
enableGridDashedLine(10f, 10f, 0f)
|
enableGridDashedLine(10f, 10f, 0f)
|
||||||
|
|
||||||
axisMaximum = 300f
|
axisMaximum = points.getMax(zoomIn)
|
||||||
axisMinimum = 100f
|
axisMinimum = points.getMin(zoomIn)
|
||||||
}
|
}
|
||||||
axisRight.isEnabled = false
|
axisRight.isEnabled = false
|
||||||
|
|
||||||
@@ -153,12 +160,16 @@ internal fun createLineChartView(isDarkTheme: Boolean, context: Context, points:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateData(points: List<Int>, chart: LineChart) {
|
private fun updateData(points: List<Int>, chart: LineChart, zoomIn: Boolean) {
|
||||||
val entries = points.mapIndexed { i, v ->
|
val entries = points.mapIndexed { i, v ->
|
||||||
Entry(-i.toFloat(), v.toFloat())
|
Entry(-i.toFloat(), v.toFloat())
|
||||||
}.reversed()
|
}.reversed()
|
||||||
|
|
||||||
with(chart) {
|
with(chart) {
|
||||||
|
axisLeft.apply {
|
||||||
|
axisMaximum = points.getMax(zoomIn)
|
||||||
|
axisMinimum = points.getMin(zoomIn)
|
||||||
|
}
|
||||||
if (data != null && data.dataSetCount > 0) {
|
if (data != null && data.dataSetCount > 0) {
|
||||||
val set1 = data!!.getDataSetByIndex(0) as LineDataSet
|
val set1 = data!!.getDataSetByIndex(0) as LineDataSet
|
||||||
set1.values = entries
|
set1.values = entries
|
||||||
@@ -169,3 +180,19 @@ private fun updateData(points: List<Int>, chart: LineChart) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun List<Int>.getMin(zoomIn: Boolean): Float {
|
||||||
|
return if (zoomIn) {
|
||||||
|
minOrNull() ?: AXIS_MIN
|
||||||
|
} else {
|
||||||
|
AXIS_MIN
|
||||||
|
}.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Int>.getMax(zoomIn: Boolean): Float {
|
||||||
|
return if (zoomIn) {
|
||||||
|
maxOrNull() ?: AXIS_MAX
|
||||||
|
} else {
|
||||||
|
AXIS_MAX
|
||||||
|
}.toFloat()
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
repository.data.onEach {
|
repository.data.onEach {
|
||||||
_state.value = WorkingState(it)
|
val zoomIn = (_state.value as? WorkingState)?.zoomIn ?: false
|
||||||
|
_state.value = WorkingState(it, zoomIn)
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +58,16 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
DisconnectEvent -> disconnect()
|
DisconnectEvent -> disconnect()
|
||||||
NavigateUpEvent -> navigationManager.navigateUp()
|
NavigateUpEvent -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
|
SwitchZoomEvent -> onZoomButtonClicked()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onZoomButtonClicked() {
|
||||||
|
(_state.value as? WorkingState)?.let {
|
||||||
|
_state.value = it.copy(zoomIn = !it.zoomIn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
repository.release()
|
repository.release()
|
||||||
navigationManager.navigateUp()
|
navigationManager.navigateUp()
|
||||||
|
|||||||
13
profile_hrs/src/main/res/drawable/ic_zoom_in.xml
Normal file
13
profile_hrs/src/main/res/drawable/ic_zoom_in.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,10h-2v2H9v-2H7V9h2V7h1v2h2v1z" />
|
||||||
|
</vector>
|
||||||
10
profile_hrs/src/main/res/drawable/ic_zoom_out.xml
Normal file
10
profile_hrs/src/main/res/drawable/ic_zoom_out.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14zM7,9h5v1L7,10z" />
|
||||||
|
</vector>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="hrs_title">HRS</string>
|
<string name="hrs_title">HRS</string>
|
||||||
|
<string name="hrs_section_data">Data</string>
|
||||||
|
<string name="hrs_zoom_icon">Icon to zoom chart in or out</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ class UARTRepository @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendText(text: String, newLineChar: MacroEol) {
|
||||||
|
manager?.send(text.parseWithNewLineChar(newLineChar))
|
||||||
|
}
|
||||||
|
|
||||||
fun runMacro(macro: UARTMacro) {
|
fun runMacro(macro: UARTMacro) {
|
||||||
macro.command?.parseWithNewLineChar(macro.newLineChar)?.let {
|
macro.command?.parseWithNewLineChar(macro.newLineChar)?.let {
|
||||||
manager?.send(it)
|
manager?.send(it)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package no.nordicsemi.android.uart.view
|
package no.nordicsemi.android.uart.view
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
@@ -15,11 +14,13 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.material.you.ScreenSection
|
import no.nordicsemi.android.material.you.*
|
||||||
import no.nordicsemi.android.theme.view.SectionTitle
|
import no.nordicsemi.android.theme.view.SectionTitle
|
||||||
import no.nordicsemi.android.uart.R
|
import no.nordicsemi.android.uart.R
|
||||||
|
import no.nordicsemi.android.uart.data.MacroEol
|
||||||
import no.nordicsemi.android.uart.data.UARTData
|
import no.nordicsemi.android.uart.data.UARTData
|
||||||
import no.nordicsemi.android.uart.data.UARTOutputRecord
|
import no.nordicsemi.android.uart.data.UARTOutputRecord
|
||||||
|
import no.nordicsemi.android.utils.EMPTY
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -33,13 +34,17 @@ internal fun UARTContentView(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
InputSection(viewState, onEvent)
|
InputSection(onEvent = onEvent)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
|
MacroSection(viewState, onEvent)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
OutputSection(state.displayMessages, onEvent)
|
OutputSection(state.displayMessages, onEvent)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { onEvent(DisconnectEvent) }
|
onClick = { onEvent(DisconnectEvent) }
|
||||||
@@ -50,7 +55,58 @@ internal fun UARTContentView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun InputSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> Unit) {
|
private fun InputSection(onEvent: (UARTViewEvent) -> Unit) {
|
||||||
|
val text = rememberSaveable { mutableStateOf(String.EMPTY) }
|
||||||
|
val hint = stringResource(id = R.string.uart_input_hint)
|
||||||
|
val checkedItem = rememberSaveable { mutableStateOf(MacroEol.values()[0]) }
|
||||||
|
|
||||||
|
val items = MacroEol.values().map {
|
||||||
|
RadioButtonItem(it.toDisplayString(), it == checkedItem.value)
|
||||||
|
}
|
||||||
|
val viewEntity = RadioGroupViewEntity(items)
|
||||||
|
|
||||||
|
ScreenSection {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
SectionTitle(resId = R.drawable.ic_input, title = stringResource(R.string.uart_input))
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.uart_macro_dialog_eol),
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
RadioButtonGroup(viewEntity) {
|
||||||
|
val i = items.indexOf(it)
|
||||||
|
checkedItem.value = MacroEol.values()[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Box(modifier = Modifier.weight(1f)) {
|
||||||
|
TextField(text = text.value, hint = hint) {
|
||||||
|
text.value = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = { onEvent(OnRunInput(text.value, checkedItem.value)) },
|
||||||
|
modifier = Modifier.padding(top = 6.dp)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.uart_send))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MacroSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> Unit) {
|
||||||
val showAddDialog = rememberSaveable { mutableStateOf(false) }
|
val showAddDialog = rememberSaveable { mutableStateOf(false) }
|
||||||
val showDeleteDialog = rememberSaveable { mutableStateOf(false) }
|
val showDeleteDialog = rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -66,7 +122,7 @@ private fun InputSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> U
|
|||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
SectionTitle(resId = R.drawable.ic_input, title = stringResource(R.string.uart_input))
|
SectionTitle(resId = R.drawable.ic_input, title = stringResource(R.string.uart_macros))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
@@ -169,7 +225,7 @@ private fun OutputSection(records: List<UARTOutputRecord>, onEvent: (UARTViewEve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
if (records.isEmpty()) {
|
if (records.isEmpty()) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package no.nordicsemi.android.uart.view
|
package no.nordicsemi.android.uart.view
|
||||||
|
|
||||||
|
import no.nordicsemi.android.uart.data.MacroEol
|
||||||
import no.nordicsemi.android.uart.data.UARTConfiguration
|
import no.nordicsemi.android.uart.data.UARTConfiguration
|
||||||
import no.nordicsemi.android.uart.data.UARTMacro
|
import no.nordicsemi.android.uart.data.UARTMacro
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ internal data class OnAddConfiguration(val name: String) : UARTViewEvent()
|
|||||||
internal object OnEditConfiguration : UARTViewEvent()
|
internal object OnEditConfiguration : UARTViewEvent()
|
||||||
internal object OnDeleteConfiguration : UARTViewEvent()
|
internal object OnDeleteConfiguration : UARTViewEvent()
|
||||||
internal data class OnRunMacro(val macro: UARTMacro) : UARTViewEvent()
|
internal data class OnRunMacro(val macro: UARTMacro) : UARTViewEvent()
|
||||||
|
internal data class OnRunInput(val text: String, val newLineChar: MacroEol) : UARTViewEvent()
|
||||||
|
|
||||||
internal object ClearOutputItems : UARTViewEvent()
|
internal object ClearOutputItems : UARTViewEvent()
|
||||||
internal object DisconnectEvent : UARTViewEvent()
|
internal object DisconnectEvent : UARTViewEvent()
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ internal class UARTViewModel @Inject constructor(
|
|||||||
OnEditConfiguration -> onEditConfiguration()
|
OnEditConfiguration -> onEditConfiguration()
|
||||||
ClearOutputItems -> repository.clearItems()
|
ClearOutputItems -> repository.clearItems()
|
||||||
OpenLogger -> repository.openLogger()
|
OpenLogger -> repository.openLogger()
|
||||||
|
is OnRunInput -> repository.sendText(event.text, event.newLineChar)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@
|
|||||||
<string name="uart_configuration_delete">Delete selected configuration.</string>
|
<string name="uart_configuration_delete">Delete selected configuration.</string>
|
||||||
<string name="uart_configuration_edit">Edit selected configuration.</string>
|
<string name="uart_configuration_edit">Edit selected configuration.</string>
|
||||||
|
|
||||||
<string name="uart_input">Macros</string>
|
<string name="uart_send">Send</string>
|
||||||
|
<string name="uart_input">Input</string>
|
||||||
|
<string name="uart_input_hint">Text to send</string>
|
||||||
|
<string name="uart_macros">Macros</string>
|
||||||
<string name="uart_output">Output</string>
|
<string name="uart_output">Output</string>
|
||||||
<string name="uart_configuration_picker_hint">Select configuration</string>
|
<string name="uart_configuration_picker_hint">Select configuration</string>
|
||||||
<string name="uart_configuration_picker_not_selected">Not selected.</string>
|
<string name="uart_configuration_picker_not_selected">Not selected.</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user