diff --git a/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java b/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java index 0c17f17b..27873e38 100644 --- a/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java +++ b/app/src/main/java/org/eclipse/kuksa/testapp/databroker/JavaDataBrokerEngine.java @@ -33,11 +33,13 @@ import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest; import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest; import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest; +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest; +import org.eclipse.kuksa.connectivity.databroker.response.VssNodeUpdateResponse; import org.eclipse.kuksa.coroutine.CoroutineCallback; import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse; import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse; -import org.eclipse.kuksa.testapp.databroker.connection.DataBrokerConnectorFactory; -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo; +import org.eclipse.kuksa.testapp.databroker.connection.factory.DataBrokerConnectorFactory; +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo; import org.eclipse.kuksa.vsscore.model.VssNode; import java.util.HashSet; @@ -124,6 +126,18 @@ public void update( dataBrokerConnection.update(request, callback); } + @Override + public void update( + @NonNull VssNodeUpdateRequest request, + @NonNull CoroutineCallback callback + ) { + if (dataBrokerConnection == null) { + return; + } + + dataBrokerConnection.update(request, callback ); + } + @Override public void subscribe(@NonNull SubscribeRequest request, @NonNull VssPathListener listener) { if (dataBrokerConnection == null) { diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt index 83040f81..614d4e15 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/KuksaDataBrokerActivity.kt @@ -38,8 +38,12 @@ import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.connectivity.databroker.response.VssNodeUpdateResponse import org.eclipse.kuksa.coroutine.CoroutineCallback import org.eclipse.kuksa.extension.entriesMetadata +import org.eclipse.kuksa.extension.firstValue +import org.eclipse.kuksa.extension.stringValue import org.eclipse.kuksa.extension.valueType import org.eclipse.kuksa.proto.v1.KuksaValV1 import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse @@ -48,16 +52,16 @@ import org.eclipse.kuksa.proto.v1.Types.Field import org.eclipse.kuksa.testapp.databroker.DataBrokerEngine import org.eclipse.kuksa.testapp.databroker.JavaDataBrokerEngine import org.eclipse.kuksa.testapp.databroker.KotlinDataBrokerEngine -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.viewmodel.ConnectionViewModel +import org.eclipse.kuksa.testapp.databroker.connection.viewmodel.ConnectionViewModel.ConnectionViewState import org.eclipse.kuksa.testapp.databroker.view.DataBrokerView -import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel.ConnectionViewState -import org.eclipse.kuksa.testapp.databroker.viewmodel.DataBrokerProperty import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputEntry import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputViewModel import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPathsViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.VssNodesViewModel +import org.eclipse.kuksa.testapp.databroker.vssnodes.viewmodel.VssNodesViewModel +import org.eclipse.kuksa.testapp.databroker.vsspaths.viewmodel.DataBrokerProperty +import org.eclipse.kuksa.testapp.databroker.vsspaths.viewmodel.VSSPathsViewModel import org.eclipse.kuksa.testapp.extension.TAG import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme @@ -167,6 +171,28 @@ class KuksaDataBrokerActivity : ComponentActivity() { dataBrokerEngine.unsubscribe(request, vssNodeListener) } + vssNodesViewModel.onUpdateSignal = { signal -> + val request = VssNodeUpdateRequest(signal) + dataBrokerEngine.update( + request, + object : CoroutineCallback() { + override fun onSuccess(result: VssNodeUpdateResponse?) { + val errorsList = result?.flatMap { it.errorsList } + errorsList?.forEach { + outputViewModel.addOutputEntry(it.toString()) + return + } + + outputViewModel.addOutputEntry(result.toString()) + } + + override fun onError(error: Throwable) { + outputViewModel.addOutputEntry("Connection to data broker failed: ${error.message}") + } + }, + ) + } + vssNodesViewModel.onGetNode = { vssNode -> fetchVssNode(vssNode) } @@ -277,7 +303,9 @@ class KuksaDataBrokerActivity : ComponentActivity() { request, object : CoroutineCallback() { override fun onSuccess(result: GetResponse?) { - val errorsList = result?.errorsList + if (result == null) return + + val errorsList = result.errorsList errorsList?.forEach { outputViewModel.addOutputEntry(it.toString()) @@ -285,13 +313,19 @@ class KuksaDataBrokerActivity : ComponentActivity() { } val outputEntry = OutputEntry() - result?.entriesList?.withIndex()?.forEach { + result.entriesList?.withIndex()?.forEach { val dataEntry = it.value val text = dataEntry.toString().substringAfter("\n") outputEntry.addMessage(text) } + outputViewModel.addOutputEntry(outputEntry) + + val updatedValue = result.firstValue?.stringValue ?: "" + val dataBrokerProperty = vssPathsViewModel.dataBrokerProperty + .copy(value = updatedValue) + vssPathsViewModel.updateDataBrokerProperty(dataBrokerProperty) } override fun onError(error: Throwable) { @@ -333,6 +367,11 @@ class KuksaDataBrokerActivity : ComponentActivity() { object : CoroutineCallback() { override fun onSuccess(result: VssNode?) { Log.d(TAG, "Fetched node: $result") + + if (result == null) return + + vssNodesViewModel.updateNode(result) + outputViewModel.addOutputEntry("Fetched node: $result") } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt index 7a4547cf..189d7eb0 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/DataBrokerEngine.kt @@ -29,10 +29,12 @@ import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.connectivity.databroker.response.VssNodeUpdateResponse import org.eclipse.kuksa.coroutine.CoroutineCallback import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.vsscore.model.VssNode @Suppress("complexity:TooManyFunctions") // required to test the api @@ -57,15 +59,20 @@ interface DataBrokerEngine { callback: CoroutineCallback, ) - fun subscribe(request: SubscribeRequest, listener: VssPathListener) + fun update( + request: VssNodeUpdateRequest, + callback: CoroutineCallback, + ) - fun unsubscribe(request: SubscribeRequest, listener: VssPathListener) + fun subscribe(request: SubscribeRequest, listener: VssPathListener) fun subscribe( request: VssNodeSubscribeRequest, vssNodeListener: VssNodeListener, ) + fun unsubscribe(request: SubscribeRequest, listener: VssPathListener) + fun unsubscribe( request: VssNodeSubscribeRequest, vssNodeListener: VssNodeListener, diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt index 6791eef2..18a34e21 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/KotlinDataBrokerEngine.kt @@ -33,11 +33,13 @@ import org.eclipse.kuksa.connectivity.databroker.request.SubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest +import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.connectivity.databroker.response.VssNodeUpdateResponse import org.eclipse.kuksa.coroutine.CoroutineCallback import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse -import org.eclipse.kuksa.testapp.databroker.connection.DataBrokerConnectorFactory -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.factory.DataBrokerConnectorFactory +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.vsscore.model.VssNode @Suppress("complexity:TooManyFunctions") @@ -110,6 +112,20 @@ class KotlinDataBrokerEngine( } } + override fun update( + request: VssNodeUpdateRequest, + callback: CoroutineCallback, + ) { + lifecycleScope.launch { + try { + val response = dataBrokerConnection?.update(request) ?: return@launch + callback.onSuccess(response) + } catch (e: DataBrokerException) { + callback.onError(e) + } + } + } + override fun subscribe(request: SubscribeRequest, listener: VssPathListener) { dataBrokerConnection?.subscribe(request, listener) } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/factory/DataBrokerConnectorFactory.kt similarity index 94% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/factory/DataBrokerConnectorFactory.kt index e30239bb..b8277423 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/DataBrokerConnectorFactory.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/factory/DataBrokerConnectorFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.connection +package org.eclipse.kuksa.testapp.databroker.connection.factory import android.content.Context import android.net.Uri @@ -29,7 +28,7 @@ import io.grpc.TlsChannelCredentials import org.eclipse.kuksa.connectivity.authentication.JsonWebToken import org.eclipse.kuksa.connectivity.databroker.DataBrokerConnector import org.eclipse.kuksa.model.TimeoutConfig -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.testapp.extension.readAsText import java.io.IOException import java.util.concurrent.TimeUnit diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/Certificate.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/Certificate.kt similarity index 94% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/Certificate.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/Certificate.kt index 53750e67..01b5c11e 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/Certificate.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/Certificate.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.model +package org.eclipse.kuksa.testapp.databroker.connection.model import android.net.Uri import androidx.compose.runtime.Immutable diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/ConnectionInfo.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/ConnectionInfo.kt similarity index 95% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/ConnectionInfo.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/ConnectionInfo.kt index 2c4788f7..87a9a82d 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/model/ConnectionInfo.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/model/ConnectionInfo.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.model +package org.eclipse.kuksa.testapp.databroker.connection.model import androidx.compose.runtime.Immutable import kotlinx.serialization.Serializable diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/AuthenticationOptionsView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/AuthenticationOptionsView.kt similarity index 94% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/AuthenticationOptionsView.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/AuthenticationOptionsView.kt index 29858205..46bf17ec 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/AuthenticationOptionsView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/AuthenticationOptionsView.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.view.connection +package org.eclipse.kuksa.testapp.databroker.connection.view.connection import android.net.Uri import androidx.compose.foundation.clickable @@ -33,7 +32,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.testapp.databroker.view.DefaultEdgePadding import org.eclipse.kuksa.testapp.databroker.view.FileSelectorSettingView import org.eclipse.kuksa.testapp.extension.fetchFileName diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/DataBrokerConnectionView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/ConnectionView.kt similarity index 94% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/DataBrokerConnectionView.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/ConnectionView.kt index fcdc428d..3ece7805 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/DataBrokerConnectionView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/ConnectionView.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.view.connection +package org.eclipse.kuksa.testapp.databroker.connection.view.connection import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility @@ -40,7 +39,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -49,19 +47,18 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.viewmodel.ConnectionViewModel +import org.eclipse.kuksa.testapp.databroker.connection.viewmodel.ConnectionViewModel.ConnectionViewState import org.eclipse.kuksa.testapp.databroker.view.DefaultEdgePadding import org.eclipse.kuksa.testapp.databroker.view.DefaultElementPadding import org.eclipse.kuksa.testapp.databroker.view.MinimumButtonWidth -import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel.ConnectionViewState import org.eclipse.kuksa.testapp.extension.compose.Headline import org.eclipse.kuksa.testapp.extension.compose.RememberCountdown import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository -@OptIn(ExperimentalComposeUiApi::class) @Composable -fun DataBrokerConnectionView(viewModel: ConnectionViewModel) { +fun ConnectionView(viewModel: ConnectionViewModel) { val keyboardController = LocalSoftwareKeyboardController.current val repositoryConnectionInfoState = @@ -224,7 +221,7 @@ private fun ConnectedPreview() { val connectionInfoRepository = ConnectionInfoRepository(LocalContext.current) val viewModel = ConnectionViewModel(connectionInfoRepository) Surface { - DataBrokerConnectionView(viewModel = viewModel) + ConnectionView(viewModel = viewModel) } } @@ -235,6 +232,6 @@ private fun DisconnectedPreview() { val viewModel = ConnectionViewModel(connectionInfoRepository) viewModel.updateConnectionState(ConnectionViewState.CONNECTING) Surface { - DataBrokerConnectionView(viewModel = viewModel) + ConnectionView(viewModel = viewModel) } } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/TlsOptionsView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/TlsOptionsView.kt similarity index 95% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/TlsOptionsView.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/TlsOptionsView.kt index b258ed6f..458d3f0e 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/connection/TlsOptionsView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/view/connection/TlsOptionsView.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2023 Contributors to the Eclipse Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.view.connection +package org.eclipse.kuksa.testapp.databroker.connection.view.connection import android.net.Uri import androidx.compose.foundation.clickable @@ -37,7 +36,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.testapp.databroker.view.DefaultEdgePadding import org.eclipse.kuksa.testapp.databroker.view.DefaultElementPadding import org.eclipse.kuksa.testapp.databroker.view.FileSelectorSettingView diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/ConnectionViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/viewmodel/ConnectionViewModel.kt similarity index 95% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/ConnectionViewModel.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/viewmodel/ConnectionViewModel.kt index 3a3168ed..9a692104 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/ConnectionViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/connection/viewmodel/ConnectionViewModel.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.viewmodel +package org.eclipse.kuksa.testapp.databroker.connection.viewmodel import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -28,7 +27,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository class ConnectionViewModel(private val connectionInfoRepository: ConnectionInfoRepository) : ViewModel() { diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt index 11bc75e4..e6a07299 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/DataBrokerView.kt @@ -21,7 +21,6 @@ package org.eclipse.kuksa.testapp.databroker.view import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -31,35 +30,20 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeight -import androidx.compose.foundation.layout.requiredWidth -import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material3.Button import androidx.compose.material3.Checkbox -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext @@ -73,24 +57,20 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase import org.eclipse.kuksa.testapp.R -import org.eclipse.kuksa.testapp.databroker.view.connection.DataBrokerConnectionView -import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionAdapter -import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionTextView -import org.eclipse.kuksa.testapp.databroker.viewmodel.ConnectionViewModel +import org.eclipse.kuksa.testapp.databroker.connection.view.connection.ConnectionView +import org.eclipse.kuksa.testapp.databroker.connection.viewmodel.ConnectionViewModel import org.eclipse.kuksa.testapp.databroker.viewmodel.OutputViewModel import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel import org.eclipse.kuksa.testapp.databroker.viewmodel.TopAppBarViewModel.DataBrokerMode -import org.eclipse.kuksa.testapp.databroker.viewmodel.VSSPathsViewModel -import org.eclipse.kuksa.testapp.databroker.viewmodel.VssNodesViewModel +import org.eclipse.kuksa.testapp.databroker.vssnodes.view.VssNodesView +import org.eclipse.kuksa.testapp.databroker.vssnodes.viewmodel.VssNodesViewModel +import org.eclipse.kuksa.testapp.databroker.vsspaths.view.VssPathsView +import org.eclipse.kuksa.testapp.databroker.vsspaths.viewmodel.VSSPathsViewModel import org.eclipse.kuksa.testapp.extension.compose.Headline import org.eclipse.kuksa.testapp.extension.compose.OverflowMenu -import org.eclipse.kuksa.testapp.extension.compose.SimpleExposedDropdownMenuBox import org.eclipse.kuksa.testapp.preferences.ConnectionInfoRepository import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme -import org.eclipse.kuksa.vss.VssVehicle -import org.eclipse.kuksa.vsscore.model.VssNode import java.time.format.DateTimeFormatter val SettingsMenuPadding = 16.dp @@ -120,13 +100,13 @@ fun DataBrokerView( ) { Column { if (!connectionViewModel.isConnected) { - DataBrokerConnectionView(connectionViewModel) + ConnectionView(connectionViewModel) } val dataBrokerMode = topAppBarViewModel.dataBrokerMode if (connectionViewModel.isConnected) { when (dataBrokerMode) { - DataBrokerMode.VSS_PATH -> DataBrokerProperties(vssPathsViewModel) - DataBrokerMode.VSS_FILE -> DataBrokerVssNodes(vssNodesViewModel) + DataBrokerMode.VSS_PATH -> VssPathsView(vssPathsViewModel) + DataBrokerMode.VSS_FILE -> VssNodesView(vssNodesViewModel) } } Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) @@ -232,209 +212,6 @@ private fun ConnectionStatusIcon( } } -@Composable -fun DataBrokerVssNodes(viewModel: VssNodesViewModel) { - Column { - Headline(name = "Generated VSS Nodes") - - val adapter = object : SuggestionAdapter { - override fun toString(item: VssNode): String { - return item.vssPath - } - } - - SuggestionTextView( - value = "Vehicle", - suggestions = viewModel.vssNodes, - adapter = adapter, - onItemSelected = { - val vssNode = it ?: VssVehicle() - viewModel.updateNode(vssNode) - }, - modifier = Modifier - .fillMaxWidth() - .padding(start = DefaultEdgePadding, end = DefaultEdgePadding), - ) - Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) - Row( - Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - Button( - onClick = { - viewModel.onGetNode(viewModel.node) - }, - modifier = Modifier.requiredWidth(80.dp), - ) { - Text(text = "Get") - } - if (viewModel.isSubscribed) { - Button(onClick = { - viewModel.subscribedNodes.remove(viewModel.node) - viewModel.onUnsubscribeNode(viewModel.node) - }) { - Text(text = "Unsubscribe") - } - } else { - Button(onClick = { - viewModel.subscribedNodes.add(viewModel.node) - viewModel.onSubscribeNode(viewModel.node) - }) { - Text(text = "Subscribe") - } - } - } - } -} - -@Composable -fun DataBrokerProperties(viewModel: VSSPathsViewModel) { - val dataBrokerProperty = viewModel.dataBrokerProperty - var expanded by remember { mutableStateOf(false) } - - Column { - Headline(name = "VSS Paths") - SuggestionTextView( - suggestions = viewModel.suggestions, - value = dataBrokerProperty.vssPath, - onValueChanged = { - val newVssProperties = dataBrokerProperty.copy( - vssPath = it, - valueType = ValueCase.VALUE_NOT_SET, - ) - viewModel.updateDataBrokerProperty(newVssProperties) - }, - label = { - Text(text = "VSS Path") - }, - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(start = DefaultEdgePadding, end = DefaultEdgePadding), - ) - Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) - Row { - TextField( - value = "${dataBrokerProperty.valueType}", - onValueChange = {}, - label = { - Text("Value Type") - }, - readOnly = true, - enabled = false, - trailingIcon = { - IconButton(onClick = { expanded = !expanded }) { - Icon( - modifier = Modifier.size(23.dp), - imageVector = Icons.Default.ArrowDropDown, - contentDescription = "More", - ) - } - }, - colors = TextFieldDefaults.colors( - disabledTextColor = Color.Black, - disabledLabelColor = Color.Black, - disabledTrailingIconColor = Color.Black, - ), - modifier = Modifier - .fillMaxWidth() - .padding(start = DefaultEdgePadding, end = DefaultEdgePadding) - .clickable { - expanded = true - }, - ) - Box(modifier = Modifier.requiredHeight(23.dp)) { - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.height(400.dp), - ) { - viewModel.valueTypes.forEach { - DropdownMenuItem( - text = { - Text(it.toString()) - }, - onClick = { - expanded = false - - val newVssProperties = dataBrokerProperty.copy(valueType = it) - viewModel.updateDataBrokerProperty(newVssProperties) - }, - ) - } - } - } - } - Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) - Row { - TextField( - value = dataBrokerProperty.value, - onValueChange = { - val newVssProperties = dataBrokerProperty.copy(value = it) - viewModel.updateDataBrokerProperty(newVssProperties) - }, - modifier = Modifier - .padding(start = DefaultEdgePadding) - .weight(1f), - singleLine = true, - label = { - Text(text = "Value") - }, - ) - Spacer(modifier = Modifier.padding(start = DefaultElementPadding, end = DefaultElementPadding)) - SimpleExposedDropdownMenuBox( - Modifier - .weight(2f) - .padding(end = DefaultEdgePadding), - label = "Field Type", - list = viewModel.fieldTypes, - onValueChange = { - val newVssProperties = dataBrokerProperty.copy(fieldTypes = setOf(it)) - viewModel.updateDataBrokerProperty(newVssProperties) - }, - ) - } - Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) - Row( - Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - Button( - onClick = { - viewModel.onGetProperty(dataBrokerProperty) - }, - modifier = Modifier.requiredWidth(80.dp), - ) { - Text(text = "Get") - } - Button( - enabled = dataBrokerProperty.valueType != ValueCase.VALUE_NOT_SET, - onClick = { - viewModel.onSetProperty(dataBrokerProperty, viewModel.datapoint) - }, - modifier = Modifier.requiredWidth(80.dp), - ) { - Text(text = "Set") - } - if (viewModel.isSubscribed) { - Button(onClick = { - viewModel.subscribedProperties.remove(dataBrokerProperty) - viewModel.onUnsubscribeProperty(dataBrokerProperty) - }) { - Text(text = "Unsubscribe") - } - } else { - Button(onClick = { - viewModel.subscribedProperties.add(dataBrokerProperty) - viewModel.onSubscribeProperty(dataBrokerProperty) - }) { - Text(text = "Subscribe") - } - } - } - } -} - @Composable fun DataBrokerOutput(viewModel: OutputViewModel, modifier: Modifier = Modifier) { val scope = rememberCoroutineScope() diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionAdapter.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionAdapter.kt index 72387096..1ee3b9a4 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionAdapter.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionAdapter.kt @@ -20,10 +20,17 @@ package org.eclipse.kuksa.testapp.databroker.view.suggestions interface SuggestionAdapter { - fun toString(item: T): String + val items: Collection + + val startingItem: T + + fun toString(item: T): String = item.toString() } -class DefaultSuggestionAdapter : SuggestionAdapter { +class DefaultSuggestionAdapter(override val items: Collection = emptyList()) : SuggestionAdapter { + override val startingItem: T + get() = items.first() + override fun toString(item: T): String { return item.toString() } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionTextView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionTextView.kt index 0d559bf8..766f2ccf 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionTextView.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/view/suggestions/SuggestionTextView.kt @@ -65,17 +65,17 @@ import org.eclipse.kuksa.testapp.R @Composable fun SuggestionTextView( - suggestions: Collection, adapter: SuggestionAdapter = DefaultSuggestionAdapter(), - value: String = "", onItemSelected: ((T?) -> Unit)? = null, onValueChanged: ((String) -> Unit)? = null, label: @Composable (() -> Unit)? = null, singleLine: Boolean = false, modifier: Modifier, ) { + val suggestions = adapter.items + var text by remember { - mutableStateOf(value) + mutableStateOf(adapter.toString(adapter.startingItem)) } val heightTextFields by remember { @@ -190,19 +190,19 @@ fun SuggestionTextView( modifier = Modifier.heightIn(max = 150.dp), ) { items( - suggestions.filter { - adapter.toString(it).lowercase().contains(text.lowercase()) + suggestions.filter { item -> + adapter.toString(item).lowercase().contains(text.lowercase()) }, - ) { + ) { item -> SuggestionItem( - item = it, - itemText = adapter.toString(it), - ) { item -> - text = adapter.toString(item) + item = item, + itemText = adapter.toString(item), + ) { suggestionItem -> + text = adapter.toString(suggestionItem) expanded = false focusManager.clearFocus() onValueChanged?.invoke(text) - onItemSelected?.invoke(it) + onItemSelected?.invoke(suggestionItem) } } } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/view/VssNodesView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/view/VssNodesView.kt new file mode 100644 index 00000000..f3f2d0b3 --- /dev/null +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/view/VssNodesView.kt @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.testapp.databroker.vssnodes.view + +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.requiredWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.eclipse.kuksa.extension.createDatapoint +import org.eclipse.kuksa.extension.valueCase +import org.eclipse.kuksa.extension.vss.copy +import org.eclipse.kuksa.testapp.databroker.view.DefaultEdgePadding +import org.eclipse.kuksa.testapp.databroker.view.DefaultElementPadding +import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionAdapter +import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionTextView +import org.eclipse.kuksa.testapp.databroker.vssnodes.viewmodel.VssNodesViewModel +import org.eclipse.kuksa.testapp.extension.compose.Headline +import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme +import org.eclipse.kuksa.vss.VssHeartRate +import org.eclipse.kuksa.vss.VssVehicle +import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal + +private class VssNodeSuggestionAdapter( + override val items: Collection, + override val startingItem: VssNode, +) : SuggestionAdapter { + override fun toString(item: VssNode): String { + return item.vssPath + } +} + +@Composable +fun VssNodesView(viewModel: VssNodesViewModel) { + val focusManager = LocalFocusManager.current + + Column { + Headline(name = "Generated VSS Nodes") + + var currentNode = viewModel.node + + val isUpdatePossible = currentNode is VssSignal<*> + val adapter = VssNodeSuggestionAdapter(viewModel.nodes, currentNode) + + SuggestionTextView( + adapter = adapter, + onItemSelected = { + val vssNode = it ?: VssVehicle() + viewModel.updateNode(vssNode) + + // Do an initial fetch of a VssSignal so the value reflects the actual one + if (vssNode is VssSignal<*>) { + viewModel.onGetNode(vssNode) + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(start = DefaultEdgePadding, end = DefaultEdgePadding), + ) + Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) + VssNodeInformation( + viewModel, + modifier = Modifier + .fillMaxWidth() + .padding(start = DefaultEdgePadding, end = DefaultEdgePadding), + onSignalChanged = { updatedSignal -> + currentNode = updatedSignal + }, + ) + Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) + Row( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button( + onClick = { + focusManager.clearFocus() + viewModel.onGetNode(currentNode) + }, + modifier = Modifier.requiredWidth(80.dp), + ) { + Text(text = "Get") + } + Button( + onClick = { + focusManager.clearFocus() + (currentNode as VssSignal<*>?)?.let { vssSignal -> + viewModel.onUpdateSignal(vssSignal) + } + }, + enabled = isUpdatePossible, + modifier = Modifier.requiredWidth(100.dp), + ) { + Text(text = "Update") + } + if (viewModel.isSubscribed) { + Button(onClick = { + focusManager.clearFocus() + viewModel.subscribedNodes.remove(currentNode) + viewModel.onUnsubscribeNode(currentNode) + }) { + Text(text = "Unsubscribe") + } + } else { + Button(onClick = { + viewModel.subscribedNodes.add(currentNode) + viewModel.onSubscribeNode(currentNode) + }) { + Text(text = "Subscribe") + } + } + } + } +} + +@Composable +private fun VssNodeInformation( + viewModel: VssNodesViewModel, + modifier: Modifier = Modifier, + isShowingInformation: Boolean = false, + onSignalChanged: (VssSignal<*>) -> Unit = {}, +) { + val vssNode = viewModel.node + val isVssSignal = vssNode is VssSignal<*> + var isShowingNodeInformation by remember { mutableStateOf(isShowingInformation) } + + Row( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + OutlinedButton(onClick = { isShowingNodeInformation = !isShowingNodeInformation }) { + Text(text = "Node Information") + } + } + + AnimatedContent( + targetState = isShowingNodeInformation, + label = "ShowingNodeInformationAnimation", + modifier = modifier, + ) { + when (it) { + true -> { + Surface(color = MaterialTheme.colorScheme.tertiary, shape = RoundedCornerShape(8.dp)) { + val boldSpanStyle = SpanStyle(fontWeight = FontWeight.Bold) + + Spacer(modifier = Modifier.padding(top = DefaultElementPadding, bottom = DefaultElementPadding)) + Column(modifier = Modifier.padding(all = DefaultElementPadding)) { + Text( + text = buildAnnotatedString { + withStyle(style = boldSpanStyle) { + append("UUID: ") + } + append(vssNode.uuid) + }, + maxLines = 2, + ) + Text( + text = buildAnnotatedString { + withStyle(style = boldSpanStyle) { + append("VSS Path: ") + } + append(vssNode.vssPath) + }, + maxLines = 1, + ) + Text( + text = buildAnnotatedString { + withStyle(style = boldSpanStyle) { + append("Type: ") + } + append(vssNode.type) + }, + maxLines = 1, + ) + Text( + text = buildAnnotatedString { + withStyle(style = boldSpanStyle) { + append("Description: ") + } + append(vssNode.description) + }, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + ) + if (isVssSignal) { + VssSignalInformation(viewModel, onSignalChanged = onSignalChanged) + } + } + } + } + + false -> { + Surface { + } + } + } + } +} + +@Composable +private fun VssSignalInformation( + viewModel: VssNodesViewModel, + modifier: Modifier = Modifier, + onSignalChanged: (VssSignal<*>) -> Unit = {}, +) { + val boldSpanStyle = SpanStyle(fontWeight = FontWeight.Bold) + val vssSignal = viewModel.node as VssSignal<*> + + var inputValue: String by remember(vssSignal, viewModel.updateCounter) { + val value = if (vssSignal.value is Array<*>) { + val valueArray = vssSignal.value as Array<*> + valueArray.joinToString() + } else { + vssSignal.value.toString() + } + + mutableStateOf(value) + } + + Text( + text = buildAnnotatedString { + withStyle(style = boldSpanStyle) { + append("Data Type: ") + } + append(vssSignal.dataType.simpleName) + }, + maxLines = 1, + ) + TextField( + value = inputValue, + onValueChange = { newValue -> + inputValue = newValue + + // Try and forget approach: Try to brute force the random input string into the VssSignal. + @Suppress("TooGenericExceptionCaught", "SwallowedException") + try { + val trimmedValue = newValue.trim() + if (trimmedValue.isEmpty()) return@TextField + + val datapoint = vssSignal.valueCase.createDatapoint(trimmedValue) + val updatedVssSignal = vssSignal.copy(datapoint) + onSignalChanged(updatedVssSignal) + } catch (e: Exception) { + // Do nothing, wrong input + } + }, + modifier = modifier + .fillMaxWidth(), + singleLine = true, + label = { + Text(text = "Value") + }, + ) +} + +@Preview(showBackground = true) +@Composable +private fun Preview() { + KuksaAppAndroidTheme { + VssNodesView(VssNodesViewModel()) + } +} + +@Preview +@Composable +private fun NodeInformationPreview() { + KuksaAppAndroidTheme { + VssNodeInformation(VssNodesViewModel(), isShowingInformation = true) + } +} + +@Preview(showBackground = true) +@Composable +private fun SignalInformationPreview() { + KuksaAppAndroidTheme { + val vssNodesViewModel = VssNodesViewModel() + vssNodesViewModel.updateNode(VssHeartRate()) + VssNodeInformation(vssNodesViewModel, isShowingInformation = true) + } +} diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/viewmodel/VssNodesViewModel.kt similarity index 83% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/viewmodel/VssNodesViewModel.kt index 1f1cb3da..133b204c 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssNodesViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vssnodes/viewmodel/VssNodesViewModel.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.viewmodel +package org.eclipse.kuksa.testapp.databroker.vssnodes.viewmodel import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -27,10 +26,12 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import org.eclipse.kuksa.vss.VssVehicle import org.eclipse.kuksa.vsscore.model.VssNode +import org.eclipse.kuksa.vsscore.model.VssSignal import org.eclipse.kuksa.vsscore.model.heritage class VssNodesViewModel : ViewModel() { var onGetNode: (node: VssNode) -> Unit = { } + var onUpdateSignal: (signal: VssSignal<*>) -> Unit = { } var onSubscribeNode: (node: VssNode) -> Unit = { } var onUnsubscribeNode: (node: VssNode) -> Unit = { } @@ -41,12 +42,16 @@ class VssNodesViewModel : ViewModel() { } private val vssVehicle = VssVehicle() - val vssNodes = listOf(vssVehicle) + vssVehicle.heritage + val nodes = listOf(vssVehicle) + vssVehicle.heritage var node: VssNode by mutableStateOf(vssVehicle) private set + var updateCounter: Int by mutableStateOf(0) + private set + fun updateNode(node: VssNode) { this.node = node + updateCounter++ } } diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/view/VssPathsView.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/view/VssPathsView.kt new file mode 100644 index 00000000..411c0f9c --- /dev/null +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/view/VssPathsView.kt @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.testapp.databroker.vsspaths.view + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.eclipse.kuksa.proto.v1.Types +import org.eclipse.kuksa.testapp.databroker.view.DefaultEdgePadding +import org.eclipse.kuksa.testapp.databroker.view.DefaultElementPadding +import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionAdapter +import org.eclipse.kuksa.testapp.databroker.view.suggestions.SuggestionTextView +import org.eclipse.kuksa.testapp.databroker.vsspaths.viewmodel.VSSPathsViewModel +import org.eclipse.kuksa.testapp.extension.compose.Headline +import org.eclipse.kuksa.testapp.extension.compose.SimpleExposedDropdownMenuBox +import org.eclipse.kuksa.testapp.ui.theme.KuksaAppAndroidTheme + +private class VssPathSuggestionAdapter( + override val items: Collection, + override val startingItem: String, +) : SuggestionAdapter + +@Composable +fun VssPathsView(viewModel: VSSPathsViewModel) { + val dataBrokerProperty = viewModel.dataBrokerProperty + val suggestionAdapter = VssPathSuggestionAdapter(viewModel.suggestions, dataBrokerProperty.vssPath) + + var expanded by remember { mutableStateOf(false) } + + Column { + Headline(name = "VSS Paths") + + SuggestionTextView( + adapter = suggestionAdapter, + onValueChanged = { + val newVssProperties = dataBrokerProperty.copy( + vssPath = it, + valueType = Types.Datapoint.ValueCase.VALUE_NOT_SET, + ) + viewModel.updateDataBrokerProperty(newVssProperties) + }, + label = { + Text(text = "VSS Path") + }, + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(start = DefaultEdgePadding, end = DefaultEdgePadding), + ) + Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) + Row { + TextField( + value = "${dataBrokerProperty.valueType}", + onValueChange = {}, + label = { + Text("Value Type") + }, + readOnly = true, + enabled = false, + trailingIcon = { + IconButton(onClick = { expanded = !expanded }) { + Icon( + modifier = Modifier.size(23.dp), + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "More", + ) + } + }, + colors = TextFieldDefaults.colors( + disabledTextColor = Color.Black, + disabledLabelColor = Color.Black, + disabledTrailingIconColor = Color.Black, + ), + modifier = Modifier + .fillMaxWidth() + .padding(start = DefaultEdgePadding, end = DefaultEdgePadding) + .clickable { + expanded = true + }, + ) + Box(modifier = Modifier.requiredHeight(23.dp)) { + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.height(400.dp), + ) { + viewModel.valueTypes.forEach { + DropdownMenuItem( + text = { + Text(it.toString()) + }, + onClick = { + expanded = false + + val newVssProperties = dataBrokerProperty.copy(valueType = it) + viewModel.updateDataBrokerProperty(newVssProperties) + }, + ) + } + } + } + } + Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) + Row { + TextField( + value = dataBrokerProperty.value, + onValueChange = { + val newVssProperties = dataBrokerProperty.copy(value = it) + viewModel.updateDataBrokerProperty(newVssProperties) + }, + modifier = Modifier + .padding(start = DefaultEdgePadding) + .weight(1f), + singleLine = true, + label = { + Text(text = "Value") + }, + ) + Spacer(modifier = Modifier.padding(start = DefaultElementPadding, end = DefaultElementPadding)) + SimpleExposedDropdownMenuBox( + Modifier + .weight(2f) + .padding(end = DefaultEdgePadding), + label = "Field Type", + list = viewModel.fieldTypes, + onValueChange = { + val newVssProperties = dataBrokerProperty.copy(fieldTypes = setOf(it)) + viewModel.updateDataBrokerProperty(newVssProperties) + }, + ) + } + Spacer(modifier = Modifier.padding(top = DefaultElementPadding)) + Row( + Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button( + onClick = { + viewModel.onGetProperty(dataBrokerProperty) + }, + modifier = Modifier.requiredWidth(80.dp), + ) { + Text(text = "Get") + } + Button( + enabled = dataBrokerProperty.valueType != Types.Datapoint.ValueCase.VALUE_NOT_SET, + onClick = { + viewModel.onSetProperty(dataBrokerProperty, viewModel.datapoint) + }, + modifier = Modifier.requiredWidth(80.dp), + ) { + Text(text = "Set") + } + if (viewModel.isSubscribed) { + Button(onClick = { + viewModel.subscribedProperties.remove(dataBrokerProperty) + viewModel.onUnsubscribeProperty(dataBrokerProperty) + }) { + Text(text = "Unsubscribe") + } + } else { + Button(onClick = { + viewModel.subscribedProperties.add(dataBrokerProperty) + viewModel.onSubscribeProperty(dataBrokerProperty) + }) { + Text(text = "Subscribe") + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun Preview() { + KuksaAppAndroidTheme { + VssPathsView(VSSPathsViewModel()) + } +} diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/viewmodel/VSSPathsViewModel.kt similarity index 98% rename from app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt rename to app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/viewmodel/VSSPathsViewModel.kt index 68479304..13bc74a2 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/viewmodel/VssPropertiesViewModel.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/databroker/vsspaths/viewmodel/VSSPathsViewModel.kt @@ -14,10 +14,9 @@ * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 - * */ -package org.eclipse.kuksa.testapp.databroker.viewmodel +package org.eclipse.kuksa.testapp.databroker.vsspaths.viewmodel import androidx.compose.runtime.Immutable import androidx.compose.runtime.derivedStateOf diff --git a/app/src/main/kotlin/org/eclipse/kuksa/testapp/preferences/ConnectionInfoRepository.kt b/app/src/main/kotlin/org/eclipse/kuksa/testapp/preferences/ConnectionInfoRepository.kt index c2ef80d4..c166db44 100644 --- a/app/src/main/kotlin/org/eclipse/kuksa/testapp/preferences/ConnectionInfoRepository.kt +++ b/app/src/main/kotlin/org/eclipse/kuksa/testapp/preferences/ConnectionInfoRepository.kt @@ -23,8 +23,8 @@ import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.dataStore import kotlinx.coroutines.flow.Flow -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfoSerializer +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfoSerializer class ConnectionInfoRepository(context: Context) { private val dataStore = context.dataStore diff --git a/app/src/test/kotlin/org/eclipse/kuksa/testapp/model/ConnectionInfoTest.kt b/app/src/test/kotlin/org/eclipse/kuksa/testapp/model/ConnectionInfoTest.kt index 494b037f..09062242 100644 --- a/app/src/test/kotlin/org/eclipse/kuksa/testapp/model/ConnectionInfoTest.kt +++ b/app/src/test/kotlin/org/eclipse/kuksa/testapp/model/ConnectionInfoTest.kt @@ -24,9 +24,9 @@ import io.kotest.matchers.shouldBe import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.eclipse.kuksa.test.kotest.Unit -import org.eclipse.kuksa.testapp.databroker.model.Certificate -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo -import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfoSerializer +import org.eclipse.kuksa.testapp.databroker.connection.model.Certificate +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfo +import org.eclipse.kuksa.testapp.databroker.connection.model.ConnectionInfoSerializer import java.io.File import java.io.FileWriter diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc73e24b..1dd68354 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] activityKtx = "1.8.2" -androidGradlePlugin = "8.3.0" # Check with detekt table first: https://detekt.dev/docs/introduction/compatibility/ +androidGradlePlugin = "8.3.1" # Check with detekt table first: https://detekt.dev/docs/introduction/compatibility/ detekt = "1.23.5" datastore = "1.0.0" constraintlayoutCompose = "1.0.1" @@ -11,7 +11,7 @@ gson = "2.10.1" kotlin = "1.9.22" kotlinpoet = "1.16.0" kotlinxSerializationJson = "1.6.1" -runtimeLivedata = "1.6.2" +runtimeLivedata = "1.6.3" symbolProcessingApi = "1.9.22-1.0.17" tomcatAnnotations = "6.0.53" ktlint = "0.0" # Maintained inside ktlint.gradle.kts @@ -23,8 +23,8 @@ kotest = "5.7.2" mockk = "1.13.7" androidxLifecycle = "2.7.0" kotlinxCoroutines = "1.7.3" -kotlinCompilerExtension = "1.5.9" -composeBom = "2024.02.01" +kotlinCompilerExtension = "1.5.10" +composeBom = "2024.02.02" jvmTarget = "17" [libraries] diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt index 3134f3ee..96c2d0f8 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/DataBrokerConnection.kt @@ -34,6 +34,7 @@ import org.eclipse.kuksa.connectivity.databroker.request.UpdateRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeFetchRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeSubscribeRequest import org.eclipse.kuksa.connectivity.databroker.request.VssNodeUpdateRequest +import org.eclipse.kuksa.connectivity.databroker.response.VssNodeUpdateResponse import org.eclipse.kuksa.connectivity.databroker.subscription.DataBrokerSubscriber import org.eclipse.kuksa.extension.TAG import org.eclipse.kuksa.extension.datapoint @@ -217,14 +218,14 @@ class DataBrokerConnection internal constructor( * Only a [VssSignal] can be updated because they have an actual value. When provided with any parent * [VssNode] then this [update] method will find all [VssSignal] children and updates their corresponding * [Types.Field] instead. - * Compared to [update] with only one [UpdateRequest], here multiple [SetResponse] will be returned - * because a [VssNode] may consists of multiple values which may need to be updated. + * Compared to [update] with only one [UpdateRequest], here multiple [SetResponse] via [VssNodeUpdateResponse] will + * be returned because a [VssNode] may consists of multiple values which may need to be updated. * * @throws DataBrokerException in case the connection to the DataBroker is no longer active * @throws IllegalArgumentException if the [VssSignal] could not be converted to a [Datapoint]. */ @Suppress("performance:SpreadOperator") // Neglectable: Field types are 1-2 elements mostly - suspend fun update(request: VssNodeUpdateRequest): Collection { + suspend fun update(request: VssNodeUpdateRequest): VssNodeUpdateResponse { val responses = mutableListOf() val vssNode = request.vssNode @@ -235,7 +236,7 @@ class DataBrokerConnection internal constructor( responses.add(response) } - return responses + return VssNodeUpdateResponse(responses) } /** diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/response/VssNodeUpdateResponse.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/response/VssNodeUpdateResponse.kt new file mode 100644 index 00000000..05993739 --- /dev/null +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/connectivity/databroker/response/VssNodeUpdateResponse.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.kuksa.connectivity.databroker.response + +import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse + +/** + * Represents a collection of [SetResponse]s. + */ +// Necessary to ensure Java compatibility with generics + suspend functions. +class VssNodeUpdateResponse internal constructor( + responses: Collection, +) : ArrayList(responses) { + internal constructor(vararg setResponse: SetResponse) : this(setResponse.toList()) +} diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt index 45c003b3..e3b3b5d5 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/DataPointExtension.kt @@ -21,7 +21,6 @@ package org.eclipse.kuksa.extension import android.util.Log import org.eclipse.kuksa.proto.v1.Types -import org.eclipse.kuksa.proto.v1.Types.BoolArray import org.eclipse.kuksa.proto.v1.Types.Datapoint import org.eclipse.kuksa.proto.v1.Types.Datapoint.ValueCase import org.eclipse.kuksa.vsscore.model.VssSignal @@ -40,10 +39,30 @@ val Types.Metadata.valueType: ValueCase * * @throws IllegalArgumentException if the [VssSignal] could not be converted to a [Datapoint]. */ -@OptIn(ExperimentalUnsignedTypes::class) val VssSignal.datapoint: Datapoint get() { - val valueCase = when (dataType) { + // TODO: Only supports string arrays for now, IntArray, DoubleArray etc. are not supported yet because + // TODO: IntArrays are custom types which to not implement the Array interface and can't be cast to it. + val stringValue = if (value::class.java.isArray) { + val valueArray = value as Array<*> + valueArray.joinToString() + } else { + value.toString() + } + + return valueCase.createDatapoint(stringValue) + } + +/** + * Converts the [VssSignal.value] into a [ValueCase] enum. The [VssSignal.dataType] is used to derive the correct + * [ValueCase]. + * + * @throws IllegalArgumentException if the [VssSignal] could not be converted to a [ValueCase]. + */ +@OptIn(ExperimentalUnsignedTypes::class) +val VssSignal.valueCase: ValueCase + get() { + return when (dataType) { String::class -> ValueCase.STRING Boolean::class -> ValueCase.BOOL Int::class -> ValueCase.INT32 @@ -52,7 +71,7 @@ val VssSignal.datapoint: Datapoint Long::class -> ValueCase.INT64 UInt::class -> ValueCase.UINT32 ULong::class -> ValueCase.UINT64 - Array::class -> ValueCase.DOUBLE + Array::class -> ValueCase.STRING_ARRAY BooleanArray::class -> ValueCase.BOOL_ARRAY IntArray::class -> ValueCase.INT32_ARRAY FloatArray::class -> ValueCase.FLOAT_ARRAY @@ -61,12 +80,8 @@ val VssSignal.datapoint: Datapoint UIntArray::class -> ValueCase.UINT32_ARRAY ULongArray::class -> ValueCase.UINT64_ARRAY - else -> throw IllegalArgumentException("Could not create datapoint for value class: ${dataType::class}!") + else -> throw IllegalArgumentException("Could not create value case for value class: ${dataType::class}!") } - - val stringValue = value.toString() - - return valueCase.createDatapoint(stringValue) } /** @@ -128,81 +143,102 @@ fun ValueCase.createDatapoint(value: String): Datapoint { datapointBuilder.boolArray = createBoolArray(value) } } catch (e: NumberFormatException) { - Log.w(TAG, "Could not convert value: $value to ValueCase: $this") + Log.w(TAG, "Could not convert value: $value to ValueCase: $this", e) datapointBuilder.string = value // Fallback to string } return datapointBuilder.build() } -private fun createBoolArray(value: String): BoolArray { - val csvValues = value.split(CSV_DELIMITER).map { it.toBoolean() } +/** + * Returns the contained value inside the [Datapoint] as a string representation. + */ +val Datapoint.stringValue: String + get() { + val value: Any = when (valueCase) { + ValueCase.STRING -> string + ValueCase.UINT32 -> uint32 + ValueCase.INT32 -> int32 + ValueCase.UINT64 -> uint64 + ValueCase.INT64 -> int64 + ValueCase.FLOAT -> float + ValueCase.DOUBLE -> double + ValueCase.BOOL -> bool + ValueCase.STRING_ARRAY -> stringArray + ValueCase.UINT32_ARRAY -> uint32Array + ValueCase.INT32_ARRAY -> int32Array + ValueCase.UINT64_ARRAY -> uint64Array + ValueCase.INT64_ARRAY -> int64Array + ValueCase.FLOAT_ARRAY -> floatArray + ValueCase.DOUBLE_ARRAY -> doubleArray + ValueCase.BOOL_ARRAY -> boolArray + ValueCase.VALUE_NOT_SET -> "" + null -> "" + } + + return value.toString() + } - val array = BoolArray.getDefaultInstance() - array.valuesList.addAll(csvValues) +private fun createBoolArray(value: String): Types.BoolArray { + val csvValues = value.split(CSV_DELIMITER).map { it.toBoolean() } - return array + return Types.BoolArray.newBuilder() + .addAllValues(csvValues) + .build() } private fun createDoubleArray(value: String): Types.DoubleArray { val csvValues = value.split(CSV_DELIMITER).map { it.toDouble() } - val array = Types.DoubleArray.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.DoubleArray.newBuilder() + .addAllValues(csvValues) + .build() } private fun createInt64Array(value: String): Types.Int64Array { val csvValues = value.split(CSV_DELIMITER).map { it.toLong() } - val array = Types.Int64Array.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.Int64Array.newBuilder() + .addAllValues(csvValues) + .build() } private fun createUInt64Array(value: String): Types.Uint64Array { val csvValues = value.split(CSV_DELIMITER).map { it.toLong() } - val array = Types.Uint64Array.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.Uint64Array.newBuilder() + .addAllValues(csvValues) + .build() } private fun createInt32Array(value: String): Types.Int32Array { val csvValues = value.split(CSV_DELIMITER).map { it.toInt() } - val array = Types.Int32Array.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.Int32Array.newBuilder() + .addAllValues(csvValues) + .build() } private fun createUInt32Array(value: String): Types.Uint32Array { val csvValues = value.split(CSV_DELIMITER).map { it.toInt() } - val array = Types.Uint32Array.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.Uint32Array.newBuilder() + .addAllValues(csvValues) + .build() } private fun createStringArray(value: String): Types.StringArray { val csvValues = value.split(CSV_DELIMITER) - val array = Types.StringArray.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.StringArray.newBuilder() + .addAllValues(csvValues) + .build() } private fun createFloatArray(value: String): Types.FloatArray { val csvValues = value.split(CSV_DELIMITER).map { it.toFloat() } - val array = Types.FloatArray.getDefaultInstance() - array.valuesList.addAll(csvValues) - - return array + return Types.FloatArray.newBuilder() + .addAllValues(csvValues) + .build() } diff --git a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt index f3700021..5eb2d51f 100644 --- a/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt +++ b/kuksa-sdk/src/main/kotlin/org/eclipse/kuksa/extension/vss/VssNodeCopyExtension.kt @@ -96,7 +96,7 @@ fun VssSignal.copy(datapoint: Datapoint): VssSignal { UINT64 -> uint64 FLOAT -> float DOUBLE -> double - STRING_ARRAY -> stringArray.valuesList + STRING_ARRAY -> stringArray.valuesList.toTypedArray() BOOL_ARRAY -> boolArray.valuesList.toBooleanArray() INT32_ARRAY -> int32Array.valuesList.toIntArray() INT64_ARRAY -> int64Array.valuesList.toLongArray() diff --git a/samples/src/main/res/values/colors.xml b/samples/src/main/res/values/colors.xml index fd65fa9f..4a83c26e 100644 --- a/samples/src/main/res/values/colors.xml +++ b/samples/src/main/res/values/colors.xml @@ -19,11 +19,5 @@ --> - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF + diff --git a/samples/src/main/res/values/themes.xml b/samples/src/main/res/values/themes.xml index 59740031..65d8355a 100644 --- a/samples/src/main/res/values/themes.xml +++ b/samples/src/main/res/values/themes.xml @@ -19,6 +19,5 @@ --> -