mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-24 01:44:23 +01:00
Add UART views.
This commit is contained in:
@@ -260,7 +260,6 @@ fun HomeView(callback: (NavDestination) -> Unit) {
|
||||
R.string.uart_module_full
|
||||
) { callback(NavDestination.UART) }
|
||||
}
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@ import no.nordicsemi.android.utils.EMPTY
|
||||
|
||||
internal data class UARTData(
|
||||
val text: String = String.EMPTY,
|
||||
val macros: List<UARTMacro> = emptyList(),
|
||||
val batteryLevel: Int = 0
|
||||
)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package no.nordicsemi.android.uart.data
|
||||
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -11,6 +14,20 @@ internal class UARTDataHolder @Inject constructor() {
|
||||
private val _data = MutableStateFlow(UARTData())
|
||||
val data = _data.asStateFlow()
|
||||
|
||||
private val _command = MutableSharedFlow<UARTServiceCommand>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
|
||||
val command = _command.asSharedFlow()
|
||||
|
||||
fun addNewMacro(macro: UARTMacro) {
|
||||
_data.tryEmit(_data.value.copy(macros = _data.value.macros + macro))
|
||||
}
|
||||
|
||||
fun deleteMacro(macro: UARTMacro) {
|
||||
val macros = _data.value.macros.toMutableList().apply {
|
||||
remove(macro)
|
||||
}
|
||||
_data.tryEmit(_data.value.copy(macros = macros))
|
||||
}
|
||||
|
||||
fun emitNewMessage(message: String) {
|
||||
_data.tryEmit(_data.value.copy(text = message))
|
||||
}
|
||||
@@ -18,4 +35,8 @@ internal class UARTDataHolder @Inject constructor() {
|
||||
fun emitNewBatteryLevel(batteryLevel: Int) {
|
||||
_data.tryEmit(_data.value.copy(batteryLevel = batteryLevel))
|
||||
}
|
||||
|
||||
fun sendNewCommand(command: UARTServiceCommand) {
|
||||
_command.tryEmit(command)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package no.nordicsemi.android.uart.data
|
||||
|
||||
data class UARTMacro(
|
||||
val alias: String,
|
||||
val command: String,
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
package no.nordicsemi.android.uart.data
|
||||
|
||||
data class UARTServiceCommand(val command: String)
|
||||
@@ -1,6 +1,9 @@
|
||||
package no.nordicsemi.android.uart.repository
|
||||
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import no.nordicsemi.android.service.ForegroundBleService
|
||||
import no.nordicsemi.android.uart.data.UARTDataHolder
|
||||
import javax.inject.Inject
|
||||
@@ -12,4 +15,12 @@ internal class UARTService : ForegroundBleService() {
|
||||
lateinit var dataHolder: UARTDataHolder
|
||||
|
||||
override val manager: UARTManager by lazy { UARTManager(this, dataHolder) }
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
dataHolder.command.onEach {
|
||||
manager.send(it.command)
|
||||
}.launchIn(lifecycleScope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package no.nordicsemi.android.uart.view
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import no.nordicsemi.android.material.you.TextField
|
||||
import no.nordicsemi.android.uart.R
|
||||
import no.nordicsemi.android.uart.data.UARTMacro
|
||||
import no.nordicsemi.android.utils.EMPTY
|
||||
|
||||
@Composable
|
||||
internal fun UARTAddMacroDialog(onDismiss: () -> Unit, onEvent: (UARTViewEvent) -> Unit) {
|
||||
val alias = remember { mutableStateOf(String.EMPTY) }
|
||||
val command = remember { mutableStateOf(String.EMPTY) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.uart_macro_dialog_title))
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
TextField(text = alias.value, hint = stringResource(id = R.string.uart_macro_dialog_alias)) {
|
||||
alias.value = it
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
|
||||
TextField(text = command.value, hint = stringResource(id = R.string.uart_macro_dialog_command)) {
|
||||
command.value = it
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onEvent(OnCreateMacro(UARTMacro(alias.value, command.value)))
|
||||
}
|
||||
) {
|
||||
Text(stringResource(id = R.string.uart_macro_dialog_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { onDismiss() }
|
||||
) {
|
||||
Text(stringResource(id = R.string.uart_macro_dialog_dismiss))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,95 @@
|
||||
package no.nordicsemi.android.uart.view
|
||||
|
||||
class UARTContentView {
|
||||
}
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
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.theme.view.ScreenSection
|
||||
import no.nordicsemi.android.theme.view.SectionTitle
|
||||
import no.nordicsemi.android.uart.R
|
||||
import no.nordicsemi.android.uart.data.UARTData
|
||||
|
||||
@Composable
|
||||
internal fun UARTContentView(state: UARTData, onEvent: (UARTViewEvent) -> Unit) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
InputSection(state, onEvent)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = { onEvent(OnDisconnectButtonClick) }
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.disconnect))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InputSection(state: UARTData, onEvent: (UARTViewEvent) -> Unit) {
|
||||
val showSearchDialog = remember { mutableStateOf(false) }
|
||||
|
||||
if (showSearchDialog.value) {
|
||||
UARTAddMacroDialog(onDismiss = { showSearchDialog.value = false }, onEvent = onEvent)
|
||||
}
|
||||
|
||||
ScreenSection {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
SectionTitle(resId = R.drawable.ic_input, title = stringResource(R.string.uart_input))
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
state.macros.forEach {
|
||||
MacroItem(macro = it, onEvent = onEvent)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
if (state.macros.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.uart_no_macros_info),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { showSearchDialog.value = true }
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.uart_add_macro))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OutputSection(state: UARTData, onEvent: (UARTViewEvent) -> Unit) {
|
||||
ScreenSection {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
SectionTitle(resId = R.drawable.ic_output, title = stringResource(R.string.uart_output))
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,54 @@
|
||||
package no.nordicsemi.android.uart.view
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import no.nordicsemi.android.theme.view.BackIconAppBar
|
||||
import no.nordicsemi.android.uart.R
|
||||
import no.nordicsemi.android.uart.data.UARTData
|
||||
import no.nordicsemi.android.uart.repository.UARTService
|
||||
import no.nordicsemi.android.uart.viewmodel.UARTViewModel
|
||||
import no.nordicsemi.android.utils.isServiceRunning
|
||||
|
||||
@Composable
|
||||
fun UARTScreen(finishAction: () -> Unit) {
|
||||
val viewModel: UARTViewModel = hiltViewModel()
|
||||
val state = viewModel.state.collectAsState().value
|
||||
val isScreenActive = viewModel.isActive.collectAsState().value
|
||||
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(isScreenActive) {
|
||||
if (!isScreenActive) {
|
||||
finishAction()
|
||||
}
|
||||
if (context.isServiceRunning(UARTService::class.java.name)) {
|
||||
val intent = Intent(context, UARTService::class.java)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect("start-service") {
|
||||
if (!context.isServiceRunning(UARTService::class.java.name)) {
|
||||
val intent = Intent(context, UARTService::class.java)
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
UARTView(state) { viewModel.onEvent(it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UARTView(state: UARTData, onEvent: (UARTViewEvent) -> Unit) {
|
||||
Column {
|
||||
BackIconAppBar(stringResource(id = R.string.uart_title)) {
|
||||
onEvent(OnDisconnectButtonClick)
|
||||
}
|
||||
|
||||
UARTContentView(state) { onEvent(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package no.nordicsemi.android.uart.view
|
||||
|
||||
import no.nordicsemi.android.uart.data.UARTMacro
|
||||
|
||||
internal sealed class UARTViewEvent
|
||||
|
||||
internal data class OnCreateMacro(val macro: UARTMacro) : UARTViewEvent()
|
||||
internal data class OnDeleteMacro(val macro: UARTMacro) : UARTViewEvent()
|
||||
|
||||
internal data class OnRunMacro(val macro: UARTMacro) : UARTViewEvent()
|
||||
|
||||
internal object OnDisconnectButtonClick : UARTViewEvent()
|
||||
@@ -0,0 +1,60 @@
|
||||
package no.nordicsemi.android.uart.view
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import no.nordicsemi.android.material.you.Card
|
||||
import no.nordicsemi.android.uart.R
|
||||
import no.nordicsemi.android.uart.data.UARTMacro
|
||||
|
||||
@Composable
|
||||
internal fun MacroItem(macro: UARTMacro, onEvent: (UARTViewEvent) -> Unit) {
|
||||
Card(backgroundColor = MaterialTheme.colorScheme.primaryContainer) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
contentDescription = stringResource(id = R.string.uart_run_macro_description),
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clickable { onEvent(OnRunMacro(macro)) }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = macro.alias,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.uart_command_field, macro.command),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(id = R.string.uart_delete_macro_description),
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clickable { onEvent(OnDeleteMacro(macro)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,26 @@
|
||||
package no.nordicsemi.android.uart.viewmodel
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
|
||||
import no.nordicsemi.android.uart.data.UARTDataHolder
|
||||
import no.nordicsemi.android.uart.data.UARTServiceCommand
|
||||
import no.nordicsemi.android.uart.view.*
|
||||
import no.nordicsemi.android.utils.exhaustive
|
||||
import javax.inject.Inject
|
||||
|
||||
class UARTViewModel : CloseableViewModel() {
|
||||
@HiltViewModel
|
||||
internal class UARTViewModel @Inject constructor(
|
||||
private val dataHolder: UARTDataHolder
|
||||
) : CloseableViewModel() {
|
||||
|
||||
val state = dataHolder.data
|
||||
|
||||
fun onEvent(event: UARTViewEvent) {
|
||||
when (event) {
|
||||
is OnCreateMacro -> dataHolder.addNewMacro(event.macro)
|
||||
is OnDeleteMacro -> dataHolder.deleteMacro(event.macro)
|
||||
OnDisconnectButtonClick -> finish()
|
||||
is OnRunMacro -> dataHolder.sendNewCommand(UARTServiceCommand(event.macro.command))
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
9
profile_uart/src/main/res/drawable/ic_input.xml
Normal file
9
profile_uart/src/main/res/drawable/ic_input.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0,0 0,4 6V9H6V6H18V18H6V15H4V18A2,2 0,0 0,6 20H18A2,2 0,0 0,20 18Z"/>
|
||||
</vector>
|
||||
9
profile_uart/src/main/res/drawable/ic_output.xml
Normal file
9
profile_uart/src/main/res/drawable/ic_output.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0,0 1,17 6V9H15V6H3V18H15V15H17V18A2,2 0,0 1,15 20H3A2,2 0,0 1,1 18Z"/>
|
||||
</vector>
|
||||
23
profile_uart/src/main/res/values/strings.xml
Normal file
23
profile_uart/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="uart_title">UART</string>
|
||||
|
||||
<string name="uart_no_macros_info">Please define a macro to send command to the device.</string>
|
||||
|
||||
<string name="uart_input">Macros</string>
|
||||
<string name="uart_output">Output</string>
|
||||
|
||||
<string name="uart_command_field">Command: %s</string>
|
||||
|
||||
<string name="uart_run_macro_description">Run macro</string>
|
||||
<string name="uart_delete_macro_description">Delete macro</string>
|
||||
|
||||
<string name="uart_add_macro">Add macro</string>
|
||||
<string name="uart_output_info">Here will be displayed read value from GATT characteristic.</string>
|
||||
|
||||
<string name="uart_macro_dialog_title">Add macro</string>
|
||||
<string name="uart_macro_dialog_alias">Alias</string>
|
||||
<string name="uart_macro_dialog_command">Command</string>
|
||||
<string name="uart_macro_dialog_confirm">Confirm</string>
|
||||
<string name="uart_macro_dialog_dismiss">Dismiss</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user