diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f22df8f5..23422dff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" + android:launchMode="singleTask" android:theme="@style/AppTheme.SplashScreen"> diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/dialog/StringListDialog.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/dialog/StringListDialog.kt index 3f7c08bb..9bb16fed 100644 --- a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/dialog/StringListDialog.kt +++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/dialog/StringListDialog.kt @@ -2,13 +2,7 @@ package no.nordicsemi.android.theme.view.dialog import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -22,7 +16,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import no.nordicsemi.android.material.you.Card import no.nordicsemi.android.theme.R @@ -51,10 +44,12 @@ fun StringListView(config: StringListDialogConfig) { ) { Text( text = config.title ?: stringResource(id = R.string.dialog).toAnnotatedString(), - fontSize = 20.sp + style = MaterialTheme.typography.headlineMedium ) } + Spacer(modifier = Modifier.size(8.dp)) + Column( modifier = Modifier .fillMaxHeight(0.8f) @@ -62,29 +57,26 @@ fun StringListView(config: StringListDialogConfig) { ) { config.items.forEachIndexed { i, entry -> - Column( + Row( modifier = Modifier .clip(RoundedCornerShape(10.dp)) .clickable { config.onResult(ItemSelectedResult(i)) } .padding(8.dp), + verticalAlignment = Alignment.CenterVertically ) { - Row { - config.leftIcon?.let { - Image( - modifier = Modifier.padding(horizontal = 4.dp), - painter = painterResource(it), - contentDescription = "Content image", - ) - } - Text( - text = entry, - fontSize = 16.sp, - modifier = Modifier - .fillMaxWidth() + config.leftIcon?.let { + Image( + modifier = Modifier.padding(horizontal = 4.dp), + painter = painterResource(it), + contentDescription = "Content image", ) } + Text( + text = entry, + modifier = Modifier.fillMaxWidth(), + style = MaterialTheme.typography.titleLarge + ) } - } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroEol.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroEol.kt index 90814100..1030355c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroEol.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroEol.kt @@ -1,6 +1,6 @@ package no.nordicsemi.android.uart.data -enum class MacroEol(val eolIndex: Int) { +enum class MacroEol(val index: Int) { LF(0), CR(1), CR_LF(2); diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt index ae1104db..f7d30ccf 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt @@ -1,6 +1,6 @@ package no.nordicsemi.android.uart.data -enum class MacroIcon(val index: Int) { +enum class MacroIcon(public val index: Int) { LEFT(0), UP(1), RIGHT(2), diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTConfiguration.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTConfiguration.kt index b3df12f9..20f31e36 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTConfiguration.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTConfiguration.kt @@ -1,10 +1,9 @@ package no.nordicsemi.android.uart.data -import no.nordicsemi.android.uart.db.XmlCommand - private const val MACROS_SIZES = 9 data class UARTConfiguration( + val id: Int?, val name: String, val macros: List = List(9) { null } ) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt index a24fb561..3980b64b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt @@ -1,8 +1,14 @@ package no.nordicsemi.android.uart.data -import no.nordicsemi.android.utils.EMPTY - internal data class UARTData( - val text: String = String.EMPTY, + val messages: List = emptyList(), val batteryLevel: Int? = null, +) { + + val displayMessages = messages.reversed().take(10) +} + +internal data class UARTOutputRecord( + val text: String, + val timestamp: Long = System.currentTimeMillis() ) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTMacro.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTMacro.kt index 94ae6a48..1e8eb8a6 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTMacro.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTMacro.kt @@ -1,3 +1,3 @@ package no.nordicsemi.android.uart.data -data class UARTMacro(val icon: MacroIcon, val command: String, val newLineChar: MacroEol) +data class UARTMacro(val icon: MacroIcon, val command: String?, val newLineChar: MacroEol) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt index e8415b66..e01e63d0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt @@ -75,10 +75,10 @@ internal class UARTManager( override fun initialize() { setNotificationCallback(txCharacteristic).asFlow().onEach { val text: String = it.getStringValue(0) ?: String.EMPTY - data.value = data.value.copy(text = text) - } + data.value = data.value.copy(messages = data.value.messages + UARTOutputRecord(text)) + }.launchIn(scope) - requestMtu(260).enqueue() + requestMtu(517).enqueue() enableNotifications(txCharacteristic).enqueue() setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow().onEach { @@ -88,7 +88,7 @@ internal class UARTManager( } override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - val service: BluetoothGattService = gatt.getService(UART_SERVICE_UUID) + val service: BluetoothGattService? = gatt.getService(UART_SERVICE_UUID) if (service != null) { rxCharacteristic = service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID) txCharacteristic = service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID) @@ -99,16 +99,13 @@ internal class UARTManager( rxCharacteristic?.let { val rxProperties: Int = it.properties writeRequest = rxProperties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 - writeCommand = - rxProperties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 + writeCommand = rxProperties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 // Set the WRITE REQUEST type when the characteristic supports it. // This will allow to send long write (also if the characteristic support it). // In case there is no WRITE REQUEST property, this manager will divide texts // longer then MTU-3 bytes into up to MTU-3 bytes chunks. - if (writeRequest) { - it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - } else { + if (!writeRequest) { useLongWrite = false } } @@ -130,7 +127,12 @@ internal class UARTManager( if (rxCharacteristic == null) return if (!TextUtils.isEmpty(text)) { scope.launchWithCatch { - val request: WriteRequest = writeCharacteristic(rxCharacteristic, text.toByteArray(), BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) + val writeType = if (useLongWrite) { + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } else { + BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE + } + val request: WriteRequest = writeCharacteristic(rxCharacteristic, text.toByteArray(), writeType) if (!useLongWrite) { request.split() } @@ -139,6 +141,10 @@ internal class UARTManager( } } + fun clearItems() { + data.value = data.value.copy(messages = emptyList()) + } + override fun getGattCallback(): BleManagerGattCallback { return UARTManagerGattCallback() } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt index e03a5cb9..1149dbf5 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt @@ -1,6 +1,5 @@ package no.nordicsemi.android.uart.data -import android.util.Log import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import no.nordicsemi.android.uart.db.* @@ -26,17 +25,17 @@ internal class UARTPersistentDataSource @Inject constructor( val serializer: Serializer = Persister(format) val configuration = serializer.read(XmlConfiguration::class.java, xml) - UARTConfiguration(configuration.name ?: "Unknown", createMacro(configuration.commands)) + UARTConfiguration(it._id, configuration.name ?: "Unknown", createMacro(configuration.commands)) } } - private fun createMacro(macros: Array): List { + private fun createMacro(macros: Array): List { return macros.map { if (it == null) { null } else { val icon = MacroIcon.create(it.iconIndex) - it.command?.let { c -> UARTMacro(icon, c, it.eol) } + UARTMacro(icon, it.command, it.eol) } } } @@ -46,13 +45,29 @@ internal class UARTPersistentDataSource @Inject constructor( val strategy: Strategy = VisitorStrategy(CommentVisitor()) val serializer: Serializer = Persister(strategy, format) val writer = StringWriter() - serializer.write(configuration, writer) + serializer.write(configuration.toXmlConfiguration(), writer) val xml = writer.toString() - configurationsDao.insert(Configuration(0, configuration.name, xml, 0)) + configurationsDao.insert(Configuration(configuration.id, configuration.name, xml, 0)) } suspend fun deleteConfiguration(configuration: UARTConfiguration) { configurationsDao.delete(configuration.name) } + + private fun UARTConfiguration.toXmlConfiguration(): XmlConfiguration { + val xmlConfiguration = XmlConfiguration() + xmlConfiguration.name = name + val commands = macros.map { macro -> + macro?.let { + XmlMacro().apply { + setEol(it.newLineChar.index) + command = it.command + iconIndex = it.icon.index + } + } + }.toTypedArray() + xmlConfiguration.commands = commands + return xmlConfiguration + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt index 92d384f0..ec60fa31 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt @@ -16,7 +16,7 @@ internal class CommentVisitor : Visitor { } override fun write(type: Type, node: NodeMap) { - if (type.type == Array::class.java) { + if (type.type == Array::class.java) { val element = node.node val builder = StringBuilder("A configuration must have 9 commands, one for each button.\n Possible icons are:") diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlCommand.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlCommand.kt deleted file mode 100644 index a48e97f2..00000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlCommand.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2015, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package no.nordicsemi.android.uart.db - -import no.nordicsemi.android.uart.data.MacroEol -import no.nordicsemi.android.uart.data.MacroIcon -import org.simpleframework.xml.Attribute -import org.simpleframework.xml.Root -import org.simpleframework.xml.Text - -@Root -internal class XmlCommand { - - /** - * Returns the command that will be sent to UART device. - * @return the command - */ - /** - * Sets the command. - * @param command the command that will be sent to UART device - */ - @Text(required = false) - var command: String? = null - /** - * Returns whether the icon is active. - * @return true if it's active - */ - /** - * Sets whether the command is active. - * @param active true to make it active - */ - @Attribute(required = false) - var isActive = false - - /** - * Returns the new line type. - * @return end of line terminator - */ - @Attribute(required = false) - var eol = MacroEol.LF - private set - - @Attribute(required = false) - private var icon = MacroIcon.LEFT - - /** - * Sets the new line type. - * @param eol end of line terminator - */ - fun setEol(eol: Int) { - this.eol = MacroEol.values()[eol] - } - /** - * Returns the icon index. - * @return the icon index - */ - /** - * Sets the icon index. - * @param index index of the icon. - */ - var iconIndex: Int - get() = icon.index - set(index) { - icon = MacroIcon.values()[index] - } -} \ No newline at end of file diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java similarity index 56% rename from profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt rename to profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java index 20c3c80d..483d8573 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java @@ -19,30 +19,57 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.uart.db -import org.simpleframework.xml.Attribute -import org.simpleframework.xml.ElementArray -import org.simpleframework.xml.Root -import org.simpleframework.xml.core.PersistenceException -import org.simpleframework.xml.core.Validate +package no.nordicsemi.android.uart.db; + +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.ElementArray; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.core.PersistenceException; +import org.simpleframework.xml.core.Validate; @Root -internal class XmlConfiguration @JvmOverloads constructor( - @field:Attribute(required = false, empty = "Unnamed") - var name: String? = "", +public class XmlConfiguration { + public static final int COMMANDS_COUNT = 9; - @field:ElementArray - var commands: Array = arrayOfNulls(COMMANDS_COUNT) -) { + @Attribute(required = false, empty = "Unnamed") + private String name; - @Validate - @Throws(PersistenceException::class) - private fun validate() { - if (commands.size != COMMANDS_COUNT) throw PersistenceException("There must be always $COMMANDS_COUNT commands in a configuration.") - } + @ElementArray + private XmlMacro[] commands = new XmlMacro[COMMANDS_COUNT]; - companion object { - const val COMMANDS_COUNT = 9 - } + /** + * Returns the field name + * + * @return optional name + */ + public String getName() { + return name; + } + + /** + * Sets the name to specified value + * @param name the new name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Returns the array of commands. There is always 9 of them. + * @return the commands array + */ + public XmlMacro[] getCommands() { + return commands; + } + + public void setCommands(XmlMacro[] commands) { + this.commands = commands; + } + + @Validate + private void validate() throws PersistenceException{ + if (commands == null || commands.length != COMMANDS_COUNT) + throw new PersistenceException("There must be always " + COMMANDS_COUNT + " commands in a configuration."); + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java new file mode 100644 index 00000000..6194189c --- /dev/null +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.uart.db; + +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Text; + +import no.nordicsemi.android.uart.data.MacroEol; +import no.nordicsemi.android.uart.data.MacroIcon; + +@Root +public class XmlMacro { + + @Text(required = false) + private String command; + + @Attribute(required = false) + private boolean active = false; + + @Attribute(required = false) + private MacroEol eol = MacroEol.LF; + + @Attribute(required = false) + private MacroIcon icon = MacroIcon.LEFT; + + /** + * Sets the command. + * @param command the command that will be sent to UART device + */ + public void setCommand(final String command) { + this.command = command; + } + + /** + * Sets whether the command is active. + * @param active true to make it active + */ + public void setActive(final boolean active) { + this.active = active; + } + + /** + * Sets the new line type. + * @param eol end of line terminator + */ + public void setEol(final int eol) { + this.eol = MacroEol.values()[eol]; + } + + /** + * Sets the icon index. + * @param index index of the icon. + */ + public void setIconIndex(final int index) { + this.icon = MacroIcon.values()[index]; + } + + /** + * Returns the command that will be sent to UART device. + * @return the command + */ + public String getCommand() { + return command; + } + + /** + * Returns whether the icon is active. + * @return true if it's active + */ + public boolean isActive() { + return active; + } + + /** + * Returns the new line type. + * @return end of line terminator + */ + public MacroEol getEol() { + return eol; + } + + /** + * Returns the icon index. + * @return the icon index + */ + public int getIconIndex() { + return icon.getIndex(); + } + /** + * Returns the EOL index. + * @return the EOL index + */ + public int getEolIndex() { + return eol.getIndex(); + } +} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 3c603001..8b7ed703 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -48,7 +48,13 @@ class UARTRepository @Inject constructor( } fun runMacro(macro: UARTMacro) { - manager?.send(macro.command.parseWithNewLineChar(macro.newLineChar)) + macro.command?.parseWithNewLineChar(macro.newLineChar)?.let { + manager?.send(it) + } + } + + fun clearItems() { + manager?.clearItems() } private suspend fun UARTManager.start(device: BluetoothDevice) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt index d1f39092..4f13112c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt @@ -1,53 +1,74 @@ package no.nordicsemi.android.uart.view import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import no.nordicsemi.android.material.you.TextField import no.nordicsemi.android.uart.R import no.nordicsemi.android.utils.EMPTY @Composable -internal fun UARTAddConfigurationDialog(onEvent: (UARTViewEvent) -> Unit) { - val name = remember { mutableStateOf(String.EMPTY) } - val isError = remember { mutableStateOf(false) } +internal fun UARTAddConfigurationDialog(onEvent: (UARTViewEvent) -> Unit, onDismiss: () -> Unit) { + val name = rememberSaveable { mutableStateOf(String.EMPTY) } + val isError = rememberSaveable { mutableStateOf(false) } - Column { - NameInput(name, isError) - - Spacer(modifier = Modifier.height(16.dp)) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End + Dialog(onDismissRequest = { onDismiss() }) { + Surface( + color = MaterialTheme.colorScheme.background, + shape = RoundedCornerShape(10.dp), + shadowElevation = 2.dp, ) { - TextButton(onClick = { onEvent(OnEditFinish) }) { - Text(stringResource(id = R.string.uart_macro_dialog_dismiss)) - } + Column(verticalArrangement = Arrangement.SpaceBetween) { + Text( + text = stringResource(id = R.string.uart_configuration_dialog_title), + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) - Spacer(modifier = Modifier.size(16.dp)) +// Spacer(modifier = Modifier.height(16.dp)) - TextButton(onClick = { - if (isNameValid(name.value)) { - onEvent(OnEditFinish) - onEvent(OnAddConfiguration(name.value)) - } else { - isError.value = true + NameInput(name, isError) + +// Spacer(modifier = Modifier.height(16.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + TextButton(onClick = { onDismiss() }) { + Text(stringResource(id = R.string.uart_macro_dialog_dismiss)) + } + + Spacer(modifier = Modifier.size(16.dp)) + + TextButton(onClick = { + if (isNameValid(name.value)) { + onDismiss() + onEvent(OnAddConfiguration(name.value)) + } else { + isError.value = true + } + }) { + Text(stringResource(id = R.string.uart_macro_dialog_confirm)) + } } - }) { - Text(stringResource(id = R.string.uart_macro_dialog_confirm)) } } } - } @Composable @@ -55,23 +76,27 @@ private fun NameInput( name: MutableState, isError: MutableState ) { - Column { + Column(modifier = Modifier.padding(16.dp)) { TextField( text = name.value, - hint = stringResource(id = R.string.uart_macro_dialog_command) + hint = stringResource(id = R.string.uart_configuration_hint) ) { isError.value = false name.value = it } - if (isError.value) { - Text( - text = stringResource(id = R.string.uart_name_empty), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.error - ) + val errorText = if (isError.value) { + stringResource(id = R.string.uart_name_empty) + } else { + String.EMPTY } + Text( + text = errorText, + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.error + ) + Spacer(modifier = Modifier.size(16.dp)) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt index 6a852c95..e64c6236 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -38,11 +39,11 @@ private const val GRID_SIZE = 5 @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun UARTAddMacroDialog(onEvent: (UARTViewEvent) -> Unit) { - val newLineChar = remember { mutableStateOf(MacroEol.LF) } - val command = remember { mutableStateOf(String.EMPTY) } - val isError = remember { mutableStateOf(false) } - val selectedIcon = remember { mutableStateOf(MacroIcon.values()[0]) } +internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> Unit) { + val newLineChar = rememberSaveable { mutableStateOf(macro?.newLineChar ?: MacroEol.LF) } + val command = rememberSaveable { mutableStateOf(macro?.command ?: String.EMPTY) } + val isError = rememberSaveable { mutableStateOf(false) } + val selectedIcon = rememberSaveable { mutableStateOf(macro?.icon ?: MacroIcon.values()[0]) } Dialog(onDismissRequest = { onEvent(OnEditFinish) }) { Surface( @@ -95,36 +96,43 @@ internal fun UARTAddMacroDialog(onEvent: (UARTViewEvent) -> Unit) { .background(background) ) } - } - Spacer(modifier = Modifier.size(16.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End - ) { - TextButton(onClick = { onEvent(OnEditFinish) }) { - Text(stringResource(id = R.string.uart_macro_dialog_dismiss)) - } + item(span = { GridItemSpan(GRID_SIZE) }) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + TextButton(onClick = { onEvent(OnEditFinish) }) { + Text(stringResource(id = R.string.uart_macro_dialog_dismiss)) + } - Spacer(modifier = Modifier.size(16.dp)) + Spacer(modifier = Modifier.size(16.dp)) - TextButton(onClick = { - if (isCommandValid(command.value)) { - onEvent( - OnCreateMacro( - UARTMacro( - selectedIcon.value, - command.value, - newLineChar.value + TextButton(onClick = { onEvent(OnDeleteMacro) }) { + Text(stringResource(id = R.string.uart_macro_dialog_delete)) + } + + Spacer(modifier = Modifier.size(16.dp)) + + TextButton(onClick = { + if (isCommandValid(command.value)) { + onEvent( + OnCreateMacro( + UARTMacro( + selectedIcon.value, + command.value, + newLineChar.value + ) + ) ) - ) - ) - } else { - isError.value = true + } else { + isError.value = true + } + }) { + Text(stringResource(id = R.string.uart_macro_dialog_confirm)) + } } - }) { - Text(stringResource(id = R.string.uart_macro_dialog_confirm)) } } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt index 9eb462df..6e23eabb 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt @@ -56,7 +56,8 @@ private fun createConfig(entries: List, onResult: (StringListDialogResul return StringListDialogConfig( title = stringResource(id = R.string.uart_configuration_picker_dialog).toAnnotatedString(), items = entries, - onResult = onResult + onResult = onResult, + leftIcon = R.drawable.ic_uart_settings ) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt index 84943c80..98dad69e 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt @@ -1,37 +1,44 @@ 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.Add +import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.Button -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource 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 +import no.nordicsemi.android.uart.data.UARTOutputRecord +import java.text.SimpleDateFormat +import java.util.* @Composable -internal fun UARTContentView(state: UARTData, viewState: UARTViewState, onEvent: (UARTViewEvent) -> Unit) { +internal fun UARTContentView( + state: UARTData, + viewState: UARTViewState, + onEvent: (UARTViewEvent) -> Unit +) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp) ) { - OutputSection(state.text) + InputSection(viewState, onEvent) Spacer(modifier = Modifier.height(16.dp)) - InputSection(viewState, onEvent) + OutputSection(state.displayMessages, onEvent) Spacer(modifier = Modifier.height(16.dp)) @@ -45,10 +52,15 @@ internal fun UARTContentView(state: UARTData, viewState: UARTViewState, onEvent: @Composable private fun InputSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> Unit) { - val showDialog = remember { mutableStateOf(false) } + val showAddDialog = rememberSaveable { mutableStateOf(false) } + val showDeleteDialog = rememberSaveable { mutableStateOf(false) } - if (showDialog.value) { - UARTAddConfigurationDialog(onEvent) + if (showAddDialog.value) { + UARTAddConfigurationDialog(onEvent) { showAddDialog.value = false } + } + + if (showDeleteDialog.value) { + DeleteConfigurationDialog(onEvent) { showDeleteDialog.value = false } } ScreenSection { @@ -63,19 +75,34 @@ private fun InputSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> U Box(modifier = Modifier.weight(1f)) { UARTConfigurationPicker(viewState, onEvent) } - - IconButton(onClick = { showDialog.value = true }) { + + IconButton(onClick = { showAddDialog.value = true }) { Icon(Icons.Default.Add, stringResource(id = R.string.uart_configuration_add)) } viewState.selectedConfiguration?.let { - IconButton(onClick = { onEvent(OnEditConfiguration) }) { - Icon(Icons.Default.Edit, stringResource(id = R.string.uart_configuration_edit)) + if (!viewState.isConfigurationEdited) { + IconButton(onClick = { onEvent(OnEditConfiguration) }) { + Icon( + Icons.Default.Edit, + stringResource(id = R.string.uart_configuration_edit) + ) + } + } else { + IconButton(onClick = { onEvent(OnEditConfiguration) }) { + Icon( + painterResource(id = R.drawable.ic_pencil_off), + stringResource(id = R.string.uart_configuration_edit) + ) + } } - IconButton(onClick = { onEvent(OnDeleteConfiguration) }) { - Icon(Icons.Default.Delete, stringResource(id = R.string.uart_configuration_delete)) + IconButton(onClick = { showDeleteDialog.value = true }) { + Icon( + Icons.Default.Delete, + stringResource(id = R.string.uart_configuration_delete) + ) } } } @@ -90,16 +117,85 @@ private fun InputSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> U } @Composable -private fun OutputSection(text: String) { +private fun DeleteConfigurationDialog(onEvent: (UARTViewEvent) -> Unit, onDismiss: () -> Unit) { + AlertDialog( + onDismissRequest = onDismiss, + title = { + Text( + text = stringResource(id = R.string.uart_delete_dialog_title), + style = MaterialTheme.typography.headlineSmall + ) + }, + text = { + Text(text = stringResource(id = R.string.uart_delete_dialog_info)) + }, + confirmButton = { + Button(onClick = { + onDismiss() + onEvent(OnDeleteConfiguration) + }) { + Text(text = stringResource(id = R.string.uart_delete_dialog_confirm)) + } + }, + dismissButton = { + Button(onClick = onDismiss) { + Text(text = stringResource(id = R.string.uart_delete_dialog_cancel)) + } + } + ) +} + +@Composable +private fun OutputSection(records: List, onEvent: (UARTViewEvent) -> Unit) { ScreenSection { Column( horizontalAlignment = Alignment.CenterHorizontally ) { - SectionTitle(resId = R.drawable.ic_output, title = stringResource(R.string.uart_output)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + SectionTitle(resId = R.drawable.ic_output, title = stringResource(R.string.uart_output), modifier = Modifier) + + Icon(Icons.Default.Delete, contentDescription = "Clear items.", modifier = Modifier.clickable { onEvent(ClearOutputItems) }) + } Spacer(modifier = Modifier.height(16.dp)) - Text(text = text.ifBlank { stringResource(id = R.string.uart_output_placeholder) }) + Column(modifier = Modifier.fillMaxWidth()) { + if (records.isEmpty()) { + Text(text = stringResource(id = R.string.uart_output_placeholder)) + } else { + records.forEach { + MessageItem(record = it) + + Spacer(modifier = Modifier.height(16.dp)) + } + } + } } } } + +@Composable +private fun MessageItem(record: UARTOutputRecord) { + Column { + Text( + text = record.timeToString(), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.outline + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = record.text, + style = MaterialTheme.typography.bodyMedium + ) + } +} + +private val datFormatter = SimpleDateFormat("dd MMMM yyyy, HH:mm:ss", Locale.ENGLISH) + +private fun UARTOutputRecord.timeToString(): String { + return datFormatter.format(timestamp) +} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTMacroView.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTMacroView.kt index 15747af4..44cd24cf 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTMacroView.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTMacroView.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.Composable 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.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -84,6 +85,7 @@ private fun MacroButton( Image( painter = painterResource(id = macro.icon.toResId()), contentDescription = stringResource(id = R.string.uart_macro_icon), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimary), modifier = Modifier .size(buttonSize) .clip(RoundedCornerShape(10.dp)) @@ -104,15 +106,17 @@ private fun EmptyButton( position: Int, onEvent: (UARTViewEvent) -> Unit ) { - Box(modifier = Modifier - .size(buttonSize) - .clip(RoundedCornerShape(10.dp)) - .clickable { - if (isEdited) { - onEvent(OnEditMacro(position)) + Box( + modifier = Modifier + .size(buttonSize) + .clip(RoundedCornerShape(10.dp)) + .clickable { + if (isEdited) { + onEvent(OnEditMacro(position)) + } } - } - .background(getBackground(isEdited))) + .background(getBackground(isEdited)) + ) } @Composable @@ -120,6 +124,6 @@ private fun getBackground(isEdited: Boolean): Color { return if (!isEdited) { MaterialTheme.colorScheme.primary } else { - MaterialTheme.colorScheme.secondary + MaterialTheme.colorScheme.tertiary } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index a20f724e..40a0c95f 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -26,7 +26,7 @@ fun UARTScreen() { val state = viewModel.state.collectAsState().value if (state.showEditDialog) { - UARTAddMacroDialog { viewModel.onEvent(it) } + UARTAddMacroDialog(state.selectedMacro) { viewModel.onEvent(it) } } Column { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt index 7f6d51c9..9e20b79d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt @@ -3,17 +3,24 @@ package no.nordicsemi.android.uart.view import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.uart.data.UARTConfiguration import no.nordicsemi.android.uart.data.UARTData +import no.nordicsemi.android.uart.data.UARTMacro internal data class UARTViewState( val editedPosition: Int? = null, - val selectedConfigurationIndex: Int? = null, + val selectedConfigurationName: String? = null, val isConfigurationEdited: Boolean = false, val configurations: List = emptyList(), val uartManagerState: HTSManagerState = NoDeviceState ) { val showEditDialog: Boolean = editedPosition != null - val selectedConfiguration: UARTConfiguration? = selectedConfigurationIndex?.let { configurations[it] } + val selectedConfiguration: UARTConfiguration? = configurations.find { selectedConfigurationName == it.name } + + val selectedMacro: UARTMacro? = selectedConfiguration?.let { configuration -> + editedPosition?.let { + configuration.macros.getOrNull(it) + } + } } internal sealed class HTSManagerState diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViewEvent.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViewEvent.kt index c95a79bb..1445ed6b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViewEvent.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViewEvent.kt @@ -7,7 +7,7 @@ internal sealed class UARTViewEvent internal data class OnEditMacro(val position: Int) : UARTViewEvent() internal data class OnCreateMacro(val macro: UARTMacro) : UARTViewEvent() -internal data class OnDeleteMacro(val macro: UARTMacro) : UARTViewEvent() +internal object OnDeleteMacro : UARTViewEvent() internal object OnEditFinish : UARTViewEvent() internal data class OnConfigurationSelected(val configuration: UARTConfiguration) : UARTViewEvent() @@ -16,6 +16,7 @@ internal object OnEditConfiguration : UARTViewEvent() internal object OnDeleteConfiguration : UARTViewEvent() internal data class OnRunMacro(val macro: UARTMacro) : UARTViewEvent() +internal object ClearOutputItems : UARTViewEvent() internal object DisconnectEvent : UARTViewEvent() internal object NavigateUp : UARTViewEvent() diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViews.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViews.kt deleted file mode 100644 index dd0a0b3d..00000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTViews.kt +++ /dev/null @@ -1,66 +0,0 @@ -package no.nordicsemi.android.uart.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.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.draw.clip -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, - ) { - Icon( - imageVector = Icons.Default.PlayArrow, - contentDescription = stringResource(id = R.string.uart_run_macro_description), - modifier = Modifier - .size(70.dp) - .padding(8.dp) - .clip(RoundedCornerShape(8.dp)) - .clickable { onEvent(OnRunMacro(macro)) } - ) - - Spacer(modifier = Modifier.size(16.dp)) - - Column(modifier = Modifier.weight(1f).padding(vertical = 8.dp)) { - Text( - text = stringResource(id = R.string.uart_macro_dialog_selected_eol, macro.newLineChar.toDisplayString()), - 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.size(16.dp)) - - Icon( - imageVector = Icons.Default.Delete, - contentDescription = stringResource(id = R.string.uart_delete_macro_description), - modifier = Modifier - .padding(8.dp) - .padding(end = 8.dp) - .size(40.dp) - .clip(RoundedCornerShape(8.dp)) - .clickable { onEvent(OnDeleteMacro(macro)) } - ) - } - } -} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index fe202335..ef7e2d6b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -64,7 +64,7 @@ internal class UARTViewModel @Inject constructor( fun onEvent(event: UARTViewEvent) { when (event) { is OnCreateMacro -> addNewMacro(event.macro) - is OnDeleteMacro -> deleteMacro(event.macro) + OnDeleteMacro -> deleteMacro() DisconnectEvent -> disconnect() is OnRunMacro -> repository.runMacro(event.macro) NavigateUp -> navigationManager.navigateUp() @@ -74,16 +74,19 @@ internal class UARTViewModel @Inject constructor( is OnAddConfiguration -> onAddConfiguration(event) OnDeleteConfiguration -> deleteConfiguration() OnEditConfiguration -> onEditConfiguration() + ClearOutputItems -> repository.clearItems() }.exhaustive } private fun onEditConfiguration() { - _state.value = _state.value.copy(isConfigurationEdited = true) + val isEdited = _state.value.isConfigurationEdited + _state.value = _state.value.copy(isConfigurationEdited = !isEdited) } private fun onAddConfiguration(event: OnAddConfiguration) { viewModelScope.launch(Dispatchers.IO) { - dataSource.saveConfiguration(UARTConfiguration(event.name)) + dataSource.saveConfiguration(UARTConfiguration(null, event.name)) + _state.value = _state.value.copy(selectedConfigurationName = event.name) } } @@ -96,7 +99,7 @@ internal class UARTViewModel @Inject constructor( } private fun onConfigurationSelected(event: OnConfigurationSelected) { - _state.value = _state.value.copy(selectedConfigurationIndex = _state.value.configurations.indexOf(event.configuration)) + _state.value = _state.value.copy(selectedConfigurationName = event.configuration.name) } private fun addNewMacro(macro: UARTMacro) { @@ -106,13 +109,6 @@ internal class UARTViewModel @Inject constructor( set(_state.value.editedPosition!!, macro) } val newConf = it.copy(macros = macros) - val newConfs = _state.value.configurations.map { - if (it.name == newConf.name) { - newConf - } else { - it - } - } dataSource.saveConfiguration(newConf) _state.value = _state.value.copy(editedPosition = null) } @@ -127,9 +123,16 @@ internal class UARTViewModel @Inject constructor( } } - private fun deleteMacro(macro: UARTMacro) { + private fun deleteMacro() { viewModelScope.launch(Dispatchers.IO) { -// dataSource.deleteMacro(macro) + _state.value.selectedConfiguration?.let { + val macros = it.macros.toMutableList().apply { + set(_state.value.editedPosition!!, null) + } + val newConf = it.copy(macros = macros) + dataSource.saveConfiguration(newConf) + _state.value = _state.value.copy(editedPosition = null) + } } } diff --git a/profile_uart/src/main/res/drawable/ic_pencil_off.xml b/profile_uart/src/main/res/drawable/ic_pencil_off.xml new file mode 100644 index 00000000..7b4658fa --- /dev/null +++ b/profile_uart/src/main/res/drawable/ic_pencil_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/profile_uart/src/main/res/drawable/ic_uart_6.xml b/profile_uart/src/main/res/drawable/ic_uart_6.xml index 39f651ba..be86d197 100644 --- a/profile_uart/src/main/res/drawable/ic_uart_6.xml +++ b/profile_uart/src/main/res/drawable/ic_uart_6.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:pathData="M11,7A2,2 0,0 0,9 9V15A2,2 0,0 0,11 17H13A2,2 0,0 0,15 15V13A2,2 0,0 0,13 11H11V9H15V7H11M11,13H13V15H11V13Z"/> diff --git a/profile_uart/src/main/res/drawable/ic_uart_7.xml b/profile_uart/src/main/res/drawable/ic_uart_7.xml index 39f651ba..1a1a8c51 100644 --- a/profile_uart/src/main/res/drawable/ic_uart_7.xml +++ b/profile_uart/src/main/res/drawable/ic_uart_7.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:pathData="M11,17L15,9V7H9V9H13L9,17"/> diff --git a/profile_uart/src/main/res/drawable/ic_uart_8.xml b/profile_uart/src/main/res/drawable/ic_uart_8.xml index 39f651ba..6235af99 100644 --- a/profile_uart/src/main/res/drawable/ic_uart_8.xml +++ b/profile_uart/src/main/res/drawable/ic_uart_8.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:pathData="M11,13H13V15H11M11,9H13V11H11M11,17H13A2,2 0,0 0,15 15V13.5A1.5,1.5 0,0 0,13.5 12A1.5,1.5 0,0 0,15 10.5V9C15,7.89 14.1,7 13,7H11A2,2 0,0 0,9 9V10.5A1.5,1.5 0,0 0,10.5 12A1.5,1.5 0,0 0,9 13.5V15C9,16.11 9.9,17 11,17"/> diff --git a/profile_uart/src/main/res/drawable/ic_uart_9.xml b/profile_uart/src/main/res/drawable/ic_uart_9.xml index 39f651ba..46eddea8 100644 --- a/profile_uart/src/main/res/drawable/ic_uart_9.xml +++ b/profile_uart/src/main/res/drawable/ic_uart_9.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:pathData="M13,17A2,2 0,0 0,15 15V9A2,2 0,0 0,13 7H11A2,2 0,0 0,9 9V11A2,2 0,0 0,11 13H13V15H9V17H13M13,11H11V9H13V11Z"/> diff --git a/profile_uart/src/main/res/values/strings.xml b/profile_uart/src/main/res/values/strings.xml index 4735fbf2..a5f7862f 100644 --- a/profile_uart/src/main/res/values/strings.xml +++ b/profile_uart/src/main/res/values/strings.xml @@ -22,11 +22,14 @@ Add macro Here will be displayed read value from GATT characteristic. + Add configuration + Configuration Add macro Alias Command Confirm Dismiss + Delete EOL: EOL: %s @@ -41,4 +44,9 @@ Provided name cannot be empty. Icon representing defined command. + + Delete configuration? + Are you sure that you want to delete this configuration? Your data will be irretrievably lost. + Confirm + Cancel