From 7b5abfed5ed83a3a6da5770321cb12a61402b8f2 Mon Sep 17 00:00:00 2001 From: Luca Bretting Date: Tue, 17 Dec 2024 23:11:39 +0100 Subject: [PATCH] refactor: fix some code smells Signed-off-by: Luca Bretting --- .github/workflows/pull_request.yml | 10 +--- .../ziofa/api/events/DataStreamProvider.kt | 5 +- .../bl/configuration/ConfigurationManager.kt | 47 +++++------------ .../ziofa/bl/events/DataStreamManager.kt | 21 +------- .../java/de/amosproj3/ziofa/ui/ZiofaApp.kt | 52 ++++++++----------- .../amosproj3/ziofa/ui/about/AboutScreen.kt | 13 ++++- .../ui/configuration/ConfigurationScreen.kt | 5 +- .../composables/EbpfIOFeatureOptions.kt | 3 +- .../composables/EbpfUprobeFeatureOptions.kt | 3 +- .../configuration/composables/ErrorScreen.kt | 4 +- .../composables/SectionTitleRow.kt | 4 +- ...gramOption.kt => BackendFeatureOptions.kt} | 0 .../ziofa/ui/navigation/ConfigurationMenu.kt | 4 +- .../ziofa/ui/navigation/HomeScreen.kt | 2 +- .../ui/navigation/composables/MenuOptions.kt | 10 ++-- .../ui/navigation/composables/ZiofaTopBar.kt | 9 +++- .../ui/navigation/data/MenuOptionData.kt | 7 +++ .../ziofa/ui/processes/ProcessesScreen.kt | 11 ++-- .../amosproj3/ziofa/ui/reset/ResetScreen.kt | 14 +++-- .../ziofa/ui/shared/ConfigurationHelpers.kt | 45 ++++++++++++++++ .../symbols/composables/SearchResultList.kt | 1 + .../symbols/composables/SymbolsSearchBar.kt | 1 + .../java/de/amosproj3/ziofa/ui/theme/Color.kt | 2 + .../java/de/amosproj3/ziofa/ui/theme/Theme.kt | 29 +---------- .../ui/visualization/VisualizationScreen.kt | 12 +++-- .../visualization/VisualizationViewModel.kt | 24 +++------ .../ui/visualization/composables/EventList.kt | 8 +-- .../composables/MetricDropdown.kt | 8 +-- .../composables/SwitchModeFab.kt | 6 +-- .../visualization/composables/VicoBarChart.kt | 14 ++--- .../composables/VicoTimeSeries.kt | 9 ++-- .../data/VisualizationDisplayMode.kt | 10 ++++ .../data/VisualizationScreenState.kt | 2 - .../ziofa/ui/visualization/utils/Constants.kt | 10 ++-- .../utils/VisualizationHelpers.kt | 18 ++++++- 35 files changed, 217 insertions(+), 206 deletions(-) rename frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/data/{EBpfProgramOption.kt => BackendFeatureOptions.kt} (100%) create mode 100644 frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/data/MenuOptionData.kt create mode 100644 frontend/app/src/main/java/de/amosproj3/ziofa/ui/shared/ConfigurationHelpers.kt create mode 100644 frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationDisplayMode.kt diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b337f8c1..f440b26c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -115,15 +115,7 @@ jobs: --parallel \ --build-cache \ -Dorg.gradle.jvmargs=-Xmx4G - - name: Detekt - run: | - cd frontend - nix develop --command ./gradlew app:detekt \ - --no-daemon \ - --parallel \ - --build-cache \ - -Dorg.gradle.jvmargs=-Xmx4G - - name: Upload SARIF to GitHub using the upload-sarif action + - name: Upload Detekt results to GitHub uses: github/codeql-action/upload-sarif@v2 if: success() || failure() with: diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/api/events/DataStreamProvider.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/api/events/DataStreamProvider.kt index 20069bb2..3b290872 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/api/events/DataStreamProvider.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/api/events/DataStreamProvider.kt @@ -7,9 +7,8 @@ package de.amosproj3.ziofa.api.events import kotlinx.coroutines.flow.Flow interface DataStreamProvider { - suspend fun counter(ebpfProgramName: String): Flow - suspend fun vfsWriteEvents(pids: List?): Flow + fun vfsWriteEvents(pids: List?): Flow - suspend fun sendMessageEvents(pids: List?): Flow + fun sendMessageEvents(pids: List?): Flow } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/configuration/ConfigurationManager.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/configuration/ConfigurationManager.kt index d1c173f3..281a8bbe 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/configuration/ConfigurationManager.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/configuration/ConfigurationManager.kt @@ -16,6 +16,7 @@ import de.amosproj3.ziofa.client.JniReferencesConfig import de.amosproj3.ziofa.client.SysSendmsgConfig import de.amosproj3.ziofa.client.UprobeConfig import de.amosproj3.ziofa.client.VfsWriteConfig +import de.amosproj3.ziofa.ui.shared.merge import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -43,6 +44,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) : .map { it ?: ConfigurationUpdate.Unknown } init { + @Suppress("TooGenericExceptionCaught") // we want to display all exceptions coroutineScope.launch { try { client = clientFactory.connect() @@ -63,7 +65,9 @@ class ConfigurationManager(val clientFactory: ClientFactory) : _localConfiguration.update { prev -> Timber.e("changeFeatureConfigurationForPIDs.prev $prev") Timber.e( - "changeFeatureConfigurationForPIDs() $vfsWriteFeature, $sendMessageFeature, $uprobesFeature, $jniReferencesFeature" + "changeFeatureConfigurationForPIDs() " + + "vfs=$vfsWriteFeature, sendMsg=$sendMessageFeature, " + + "uprobes=$uprobesFeature, jni=$jniReferencesFeature" ) // the configuration shall not be changed from the UI if there is none received from // backend @@ -71,40 +75,10 @@ class ConfigurationManager(val clientFactory: ClientFactory) : val previousConfiguration = prev.configuration previousConfiguration .copy( - vfsWrite = - vfsWriteFeature?.let { requestedChanges -> - previousConfiguration.vfsWrite.updatePIDs( - pidsToAdd = - if (enable) requestedChanges.entries.entries else setOf(), - pidsToRemove = - if (!enable) requestedChanges.entries.entries else setOf(), - ) - } ?: previousConfiguration.vfsWrite, - sysSendmsg = - sendMessageFeature?.let { requestedChanges -> - previousConfiguration.sysSendmsg.updatePIDs( - pidsToAdd = - if (enable) requestedChanges.entries.entries else setOf(), - pidsToRemove = - if (!enable) requestedChanges.entries.entries else setOf(), - ) - } ?: previousConfiguration.sysSendmsg, - uprobes = - uprobesFeature.let { requestedChanges -> - if (requestedChanges == null) - return@let previousConfiguration.uprobes - previousConfiguration.uprobes.updateUProbes( - pidsToAdd = if (enable) requestedChanges else listOf(), - pidsToRemove = if (!enable) requestedChanges else listOf(), - ) - }, - jniReferences = - jniReferencesFeature?.let { requestedChanges -> - previousConfiguration.jniReferences.updatePIDs( - pidsToAdd = if (enable) requestedChanges.pids else listOf(), - pidsToRemove = if (!enable) requestedChanges.pids else listOf(), - ) - } ?: previousConfiguration.jniReferences, + vfsWrite = previousConfiguration.merge(vfsWriteFeature, enable), + sysSendmsg = previousConfiguration.merge(sendMessageFeature, enable), + uprobes = previousConfiguration.merge(uprobesFeature, enable), + jniReferences = previousConfiguration.merge(jniReferencesFeature, enable), ) .also { Timber.i("new local configuration = $it") } .let { ConfigurationUpdate.Valid(it) } @@ -128,6 +102,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) : } } + @Suppress("TooGenericExceptionCaught", "SwallowedException") // initialization mechanism private suspend fun initializeConfigurations() { val initializedConfiguration = try { @@ -139,6 +114,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) : } // TODO this should be handled on the backend + @Suppress("TooGenericExceptionCaught") // we want to display all exceptions private suspend fun getOrCreateInitialConfiguration(): ConfigurationUpdate { return try { // the config may not be initialized, we should try initializing it @@ -162,6 +138,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) : } ?: Timber.e("unsubmittedConfiguration == null -> this should never happen") } + @Suppress("TooGenericExceptionCaught") // we want to display all exceptions private suspend fun getFromBackend(): ConfigurationUpdate { return try { (client?.getConfiguration()?.let { ConfigurationUpdate.Valid(it) } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/events/DataStreamManager.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/events/DataStreamManager.kt index c0483ba6..8a1d055d 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/events/DataStreamManager.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/events/DataStreamManager.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.shareIn -import timber.log.Timber class DataStreamManager(private val clientFactory: ClientFactory, coroutineScope: CoroutineScope) : DataStreamProvider { @@ -26,23 +25,7 @@ class DataStreamManager(private val clientFactory: ClientFactory, coroutineScope flow { clientFactory.connect().initStream().collect { emit(it) } } .shareIn(coroutineScope, SharingStarted.Lazily) - override suspend fun counter(ebpfProgramName: String): Flow { - return clientFactory - .connect() - .also { - try { - it.load() - // default wifi interface on android, now configurable - it.attach("wlan0") - it.startCollecting() - } catch (e: Exception) { - Timber.e(e.stackTraceToString()) - } - } - .serverCount() - } - - override suspend fun vfsWriteEvents(pids: List?): Flow = + override fun vfsWriteEvents(pids: List?): Flow = dataFlow .mapNotNull { it as? Event.VfsWrite } .filter { it.pid.isGlobalRequestedOrPidConfigured(pids) } @@ -55,7 +38,7 @@ class DataStreamManager(private val clientFactory: ClientFactory, coroutineScope ) } - override suspend fun sendMessageEvents(pids: List?): Flow = + override fun sendMessageEvents(pids: List?): Flow = dataFlow .mapNotNull { it as? Event.SysSendmsg } .filter { it.pid.isGlobalRequestedOrPidConfigured(pids) } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/ZiofaApp.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/ZiofaApp.kt index 4cb76681..b3d55d10 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/ZiofaApp.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/ZiofaApp.kt @@ -34,7 +34,19 @@ import de.amosproj3.ziofa.ui.visualization.VisualizationScreen val GLOBAL_CONFIGURATION_ROUTE = "${Routes.IndividualConfiguration.name}?displayName=${Uri.encode("all processes")}?pids=-1" +val PIDS_ARG = + navArgument("pids") { + type = NavType.StringType + nullable = true + } +val DISPLAY_NAME_ARG = + navArgument("displayName") { + type = NavType.StringType + nullable = true + } + /** Main application composable. All calls to [NavController] should happen here. */ +@Suppress("ModifierMissing, LongMethod") // Top level composable @Composable fun ZIOFAApp() { val navController = rememberNavController() @@ -56,16 +68,16 @@ fun ZIOFAApp() { } screenWithDefaultAnimations(Routes.Reset.name) { ResetScreen( - Modifier.padding(innerPadding), afterResetConfirmed = { navController.popBackStack() }, + modifier = Modifier.padding(innerPadding), ) } screenWithDefaultAnimations(Routes.Configuration.name) { ConfigurationMenu( - Modifier.padding(innerPadding), toProcesses = { navController.navigate(Routes.Processes.name) }, toGlobalConfiguration = { navController.navigate(GLOBAL_CONFIGURATION_ROUTE) }, toReset = { navController.navigate(Routes.Reset.name) }, + modifier = Modifier.padding(innerPadding), ) } screenWithDefaultAnimations(Routes.Visualize.name) { @@ -77,28 +89,17 @@ fun ZIOFAApp() { screenWithDefaultAnimations(Routes.Processes.name) { ProcessesScreen( Modifier.padding(innerPadding), - onClickEdit = { - navController.navigate(it.toConfigurationScreenRouteForComponent()) + onClickEdit = { component -> + navController.navigate(component.toConfigurationScreenRouteForComponent()) }, ) } parameterizedScreen( "${Routes.IndividualConfiguration.name}?displayName={displayName}?pids={pids}", - arguments = - listOf( - navArgument("displayName") { - type = NavType.StringType - nullable = true - }, - navArgument("pids") { - type = NavType.StringType - nullable = true - }, - ), + arguments = listOf(DISPLAY_NAME_ARG, PIDS_ARG), ) { ConfigurationScreen( Modifier.padding(innerPadding), - onBack = { navController.popBackStack() }, pids = it.arguments?.getString("pids")?.deserializePIDs()?.validPIDsOrNull(), onAddUprobeSelected = { navController.navigate(it.arguments.copyToSymbolsRoute()) @@ -108,24 +109,17 @@ fun ZIOFAApp() { parameterizedScreen( "${Routes.Symbols.name}?displayName={displayName}?pids={pids}", - arguments = - listOf( - navArgument("displayName") { - type = NavType.StringType - nullable = true - }, - navArgument("pids") { - type = NavType.StringType - nullable = true - }, - ), + arguments = listOf(DISPLAY_NAME_ARG, PIDS_ARG), ) { SymbolsScreen( modifier = Modifier.padding(innerPadding), onSymbolsSubmitted = { navController.popBackStack() }, pids = - it.arguments?.getString("pids")?.deserializePIDs()?.validPIDsOrNull() - ?: listOf(), + it.arguments + ?.getString("pids") + ?.deserializePIDs() + ?.validPIDsOrNull() + .orEmpty(), ) } } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/about/AboutScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/about/AboutScreen.kt index 08719fcf..3b6fb14b 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/about/AboutScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/about/AboutScreen.kt @@ -12,8 +12,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp const val PRODUCT_MISSION = - "ZIOFA (Zero Instrumentation Observability for Android) aims to implement observability use cases relevant to performance specified by our industry partner using eBPF. Examples include tracing long-running blocking calls, leaking JNI indirect references or signals like SIGKILL sent to processes, all without instrumenting the observed application itself.\n" + - "The eBPF programs are loaded and unloaded using a backend daemon running as root that will collect metrics and send them to a client. For displaying these metrics to the user, we are implementing an on-device UI that can display visualizations for these use cases and allow for configuration of the enabled use cases, but using a decoupled Client SDK so that future work may easily make the data accessible the external processing." + "ZIOFA (Zero Instrumentation Observability for Android) aims to implement observability use " + + "cases relevant to performance specified by our industry partner using eBPF. " + + "Examples include tracing long-running blocking calls, leaking JNI indirect" + + "references or signals like SIGKILL sent to processes, all without instrumenting" + + " the observed application itself.\n" + + "The eBPF programs are loaded and unloaded using a backend daemon running as root that " + + "will collect metrics and send them to a client. For displaying these metrics to " + + "the user, we are implementing an on-device UI that can display visualizations for" + + " these use cases and allow for configuration of the enabled use cases, but using a " + + "decoupled Client SDK so that future work may easily make the data accessible the " + + "external processing." /** * Screen containing information about the project. Might delete later if we need space for another diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationScreen.kt index 1915d908..5c313eb6 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationScreen.kt @@ -35,7 +35,6 @@ import org.koin.core.parameter.parametersOf @Composable fun ConfigurationScreen( modifier: Modifier = Modifier, - onBack: () -> Unit = {}, onAddUprobeSelected: () -> Unit = {}, pids: List? = listOf(), ) { @@ -62,9 +61,7 @@ fun ConfigurationScreen( SectionTitleRow("Uprobes") EbpfUprobeFeatureOptions( options = - state.options.mapNotNull { - if (it is BackendFeatureOptions.UprobeOption) it else null - }, + state.options.mapNotNull { it as? BackendFeatureOptions.UprobeOption }, onOptionDeleted = { option -> viewModel.optionChanged(option, active = false) }, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfIOFeatureOptions.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfIOFeatureOptions.kt index ae6171bc..2186e8dc 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfIOFeatureOptions.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfIOFeatureOptions.kt @@ -23,8 +23,9 @@ import de.amosproj3.ziofa.ui.configuration.data.BackendFeatureOptions fun EbpfIOFeatureOptions( options: List, onOptionChanged: (BackendFeatureOptions, Boolean) -> Unit, + modifier: Modifier = Modifier, ) { - LazyColumn(modifier = Modifier.padding(horizontal = 20.dp, vertical = 15.dp).fillMaxWidth()) { + LazyColumn(modifier = modifier.padding(horizontal = 20.dp, vertical = 15.dp).fillMaxWidth()) { items(options) { option -> Row( modifier = Modifier.fillMaxWidth(), diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfUprobeFeatureOptions.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfUprobeFeatureOptions.kt index 95abf808..685eb01a 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfUprobeFeatureOptions.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/EbpfUprobeFeatureOptions.kt @@ -28,8 +28,9 @@ fun EbpfUprobeFeatureOptions( options: List, onOptionDeleted: (BackendFeatureOptions.UprobeOption) -> Unit, onAddUprobeSelected: () -> Unit, + modifier: Modifier = Modifier, ) { - LazyColumn(modifier = Modifier.padding(horizontal = 20.dp, vertical = 15.dp).fillMaxSize()) { + LazyColumn(modifier = modifier.padding(horizontal = 20.dp, vertical = 15.dp).fillMaxSize()) { items(options) { option -> Row( modifier = Modifier.fillMaxWidth(), diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/ErrorScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/ErrorScreen.kt index fd9ce2f9..c1044d6d 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/ErrorScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/ErrorScreen.kt @@ -25,6 +25,8 @@ import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp import kotlin.system.exitProcess +const val TITLE_TEXT_SIZE = 25f + @Preview(device = Devices.AUTOMOTIVE_1024p) @Composable fun ErrorScreen(error: String = "No error message available") { @@ -36,7 +38,7 @@ fun ErrorScreen(error: String = "No error message available") { Text( text = "Error while communicating with backend", color = Color.Red, - fontSize = TextUnit(25f, TextUnitType.Sp), + fontSize = TextUnit(TITLE_TEXT_SIZE, TextUnitType.Sp), ) Text(text = error) } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/SectionTitleRow.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/SectionTitleRow.kt index 5ba4279c..b65d25fb 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/SectionTitleRow.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/composables/SectionTitleRow.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @Composable -fun SectionTitleRow(title: String) { - Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.padding(bottom = 10.dp)) { +fun SectionTitleRow(title: String, modifier: Modifier = Modifier) { + Row(horizontalArrangement = Arrangement.Center, modifier = modifier.padding(bottom = 10.dp)) { Text(title, fontWeight = FontWeight.Bold) } HorizontalDivider(thickness = 3.dp) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/data/EBpfProgramOption.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/data/BackendFeatureOptions.kt similarity index 100% rename from frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/data/EBpfProgramOption.kt rename to frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/data/BackendFeatureOptions.kt diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/ConfigurationMenu.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/ConfigurationMenu.kt index 4f1b8fd9..99158f15 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/ConfigurationMenu.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/ConfigurationMenu.kt @@ -14,16 +14,16 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import de.amosproj3.ziofa.ui.navigation.composables.MenuOptionData import de.amosproj3.ziofa.ui.navigation.composables.MenuOptions import de.amosproj3.ziofa.ui.navigation.data.Emoji +import de.amosproj3.ziofa.ui.navigation.data.MenuOptionData @Composable fun ConfigurationMenu( - modifier: Modifier = Modifier, toProcesses: () -> Unit, toGlobalConfiguration: () -> Unit, toReset: () -> Unit, + modifier: Modifier = Modifier, ) { Box( diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/HomeScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/HomeScreen.kt index 724af328..f2561e4e 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/HomeScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/HomeScreen.kt @@ -16,9 +16,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import de.amosproj3.ziofa.ui.navigation.composables.MenuOptionData import de.amosproj3.ziofa.ui.navigation.composables.MenuOptions import de.amosproj3.ziofa.ui.navigation.data.Emoji +import de.amosproj3.ziofa.ui.navigation.data.MenuOptionData /** Static home screen for navigation */ @Composable diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/MenuOptions.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/MenuOptions.kt index cdb44434..0cba11ee 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/MenuOptions.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/MenuOptions.kt @@ -22,11 +22,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp +import de.amosproj3.ziofa.ui.navigation.data.MenuOptionData -data class MenuOptionData(val title: String, val logoEmoji: String, val onClick: () -> Unit) +private const val CARD_EMOJI_SIZE = 120f +private const val CARD_TITLE_TEXT_SIZE = 40f @Composable -fun MenuOptions(modifier: Modifier = Modifier, menuOptions: List) { +fun MenuOptions(menuOptions: List, modifier: Modifier = Modifier) { Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, @@ -65,10 +67,10 @@ fun MenuCardWithIcon( ) { Text( emoji, - fontSize = TextUnit(120f, TextUnitType.Sp), + fontSize = TextUnit(CARD_EMOJI_SIZE, TextUnitType.Sp), modifier = Modifier.padding(bottom = 20.dp), ) - Text(text, fontSize = TextUnit(40f, TextUnitType.Sp)) + Text(text, fontSize = TextUnit(CARD_TITLE_TEXT_SIZE, TextUnitType.Sp)) } } } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/ZiofaTopBar.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/ZiofaTopBar.kt index 7bfd3006..e493c9a9 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/ZiofaTopBar.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/composables/ZiofaTopBar.kt @@ -26,13 +26,18 @@ import androidx.compose.ui.unit.dp /** Top bar containing the app name and AMOS text */ @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ZiofaTopBar(screenName: String, showBackButton: Boolean = true, onBack: () -> Unit = {}) { +fun ZiofaTopBar( + screenName: String, + modifier: Modifier = Modifier, + showBackButton: Boolean = true, + onBack: () -> Unit = {}, +) { TopAppBar( { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth(), ) { Row(verticalAlignment = Alignment.CenterVertically) { if (showBackButton) { diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/data/MenuOptionData.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/data/MenuOptionData.kt new file mode 100644 index 00000000..4cd2f1b4 --- /dev/null +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/navigation/data/MenuOptionData.kt @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2024 Luca Bretting +// +// SPDX-License-Identifier: MIT + +package de.amosproj3.ziofa.ui.navigation.data + +data class MenuOptionData(val title: String, val logoEmoji: String, val onClick: () -> Unit) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/processes/ProcessesScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/processes/ProcessesScreen.kt index efd001e9..3894730a 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/processes/ProcessesScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/processes/ProcessesScreen.kt @@ -34,7 +34,7 @@ import org.koin.androidx.compose.koinViewModel @Composable fun ProcessesScreen( - modifier: Modifier, + modifier: Modifier = Modifier, viewModel: ProcessesViewModel = koinViewModel(), onClickEdit: (RunningComponent) -> Unit, ) { @@ -51,7 +51,9 @@ fun ProcessesScreen( ProcessesHeader() if (options.isNotEmpty()) { LazyColumn(modifier = Modifier.padding(horizontal = 20.dp).fillMaxSize()) { - items(options) { option -> ProcessListRow(option, onClickEdit = onClickEdit) } + items(options) { option -> + ProcessListRow(option = option, onClickEdit = onClickEdit) + } } } else { Box(modifier.fillMaxSize()) { @@ -64,13 +66,12 @@ fun ProcessesScreen( @Composable fun ProcessListRow( + modifier: Modifier = Modifier, option: RunningComponent, - onClickProcessInfo: (RunningComponent) -> Unit = - {}, // TODO implement modal with info about processes onClickEdit: (RunningComponent) -> Unit = {}, ) { Row( - modifier = Modifier.fillMaxSize().padding(vertical = 10.dp), + modifier = modifier.fillMaxSize().padding(vertical = 10.dp), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, ) { diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/reset/ResetScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/reset/ResetScreen.kt index 510047f4..30860c97 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/reset/ResetScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/reset/ResetScreen.kt @@ -27,18 +27,21 @@ const val RESET_WARNING = This will set all features to disabled for all processes! """ +const val CONFIRM_BUTTON_TEXT_SIZE = 20f +const val WARNING_TEXT_SIZE = 40f + @Composable fun ResetScreen( - modifier: Modifier, - viewModel: ResetViewModel = koinViewModel(), afterResetConfirmed: () -> Unit, + modifier: Modifier = Modifier, + viewModel: ResetViewModel = koinViewModel(), ) { Box(modifier = modifier.fillMaxSize()) { Text( RESET_WARNING, modifier = Modifier.align(Alignment.Center), - fontSize = TextUnit(40f, TextUnitType.Sp), + fontSize = TextUnit(WARNING_TEXT_SIZE, TextUnitType.Sp), fontWeight = FontWeight.Bold, ) Button( @@ -48,7 +51,10 @@ fun ResetScreen( afterResetConfirmed() }, ) { - Text("Reset configuration", fontSize = TextUnit(40f, TextUnitType.Sp)) + Text( + "Reset configuration", + fontSize = TextUnit(CONFIRM_BUTTON_TEXT_SIZE, TextUnitType.Sp), + ) } } } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/shared/ConfigurationHelpers.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/shared/ConfigurationHelpers.kt new file mode 100644 index 00000000..3e14752b --- /dev/null +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/shared/ConfigurationHelpers.kt @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 Luca Bretting +// +// SPDX-License-Identifier: MIT + +package de.amosproj3.ziofa.ui.shared + +import de.amosproj3.ziofa.bl.configuration.updatePIDs +import de.amosproj3.ziofa.bl.configuration.updateUProbes +import de.amosproj3.ziofa.client.Configuration +import de.amosproj3.ziofa.client.JniReferencesConfig +import de.amosproj3.ziofa.client.SysSendmsgConfig +import de.amosproj3.ziofa.client.UprobeConfig +import de.amosproj3.ziofa.client.VfsWriteConfig + +fun Configuration.merge(vfsWriteConfig: VfsWriteConfig?, enable: Boolean) = + vfsWriteConfig?.let { requestedChanges -> + this.vfsWrite.updatePIDs( + pidsToAdd = if (enable) requestedChanges.entries.entries else setOf(), + pidsToRemove = if (!enable) requestedChanges.entries.entries else setOf(), + ) + } ?: this.vfsWrite + +fun Configuration.merge(sysSendmsgConfig: SysSendmsgConfig?, enable: Boolean) = + sysSendmsgConfig?.let { requestedChanges -> + this.sysSendmsg.updatePIDs( + pidsToAdd = if (enable) requestedChanges.entries.entries else setOf(), + pidsToRemove = if (!enable) requestedChanges.entries.entries else setOf(), + ) + } ?: this.sysSendmsg + +fun Configuration.merge(uprobeConfigs: List?, enable: Boolean) = + uprobeConfigs?.let { requestedChanges -> + this.uprobes.updateUProbes( + pidsToAdd = if (enable) requestedChanges else listOf(), + pidsToRemove = if (!enable) requestedChanges else listOf(), + ) + } ?: this.uprobes + +fun Configuration.merge(jniReferencesConfig: JniReferencesConfig?, enable: Boolean) = + jniReferencesConfig?.let { requestedChanges -> + this.jniReferences.updatePIDs( + pidsToAdd = if (enable) requestedChanges.pids else listOf(), + pidsToRemove = if (!enable) requestedChanges.pids else listOf(), + ) + } ?: this.jniReferences diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SearchResultList.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SearchResultList.kt index 0c8a23db..56baee3d 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SearchResultList.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SearchResultList.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import de.amosproj3.ziofa.ui.symbols.data.SymbolsEntry +@Suppress("MagicNumber") // does not improve readability @Composable fun SearchResultList( symbols: Map, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SymbolsSearchBar.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SymbolsSearchBar.kt index cff6013f..7c1e9168 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SymbolsSearchBar.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/symbols/composables/SymbolsSearchBar.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +@Suppress("MagicNumber") // does not improve readability @Composable fun SymbolsSearchBar( value: String, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Color.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Color.kt index f437422b..068b9aa7 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Color.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Color.kt @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: MIT +@file:Suppress("MagicNumber") + package de.amosproj3.ziofa.ui.theme import androidx.compose.ui.graphics.Color diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Theme.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Theme.kt index 824de985..3ecb6845 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Theme.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/theme/Theme.kt @@ -6,40 +6,13 @@ package de.amosproj3.ziofa.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext -private val DarkColorScheme = - darkColorScheme(primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80) - -private val LightColorScheme = - lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40, - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ - ) - @Composable -fun ZIOFATheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit, -) { +fun ZIOFATheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val context = LocalContext.current val colorScheme = if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationScreen.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationScreen.kt index d3a3c32b..8eea6fa1 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationScreen.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationScreen.kt @@ -76,7 +76,7 @@ fun VisualizationScreen( if (state is VisualizationScreenState.MetricSelectionValid) SwitchModeFab( - Modifier.align(Alignment.BottomEnd), + modifier = Modifier.align(Alignment.BottomEnd), onClick = { viewModel.switchMode() }, activeDisplayMode = state.displayMode, ) @@ -84,8 +84,8 @@ fun VisualizationScreen( } @Composable -fun SelectMetricPrompt() { - Box(Modifier.fillMaxSize()) { +fun SelectMetricPrompt(modifier: Modifier = Modifier) { + Box(modifier.fillMaxSize()) { Text( "Please make a selection!", Modifier.align(Alignment.Center), @@ -115,8 +115,9 @@ fun MetricSelection( filterSelected: (DropdownOption) -> Unit, metricSelected: (DropdownOption) -> Unit, timeframeSelected: (DropdownOption) -> Unit, + modifier: Modifier = Modifier, ) { - Row(Modifier.fillMaxWidth()) { + Row(modifier.fillMaxWidth()) { MetricDropdown( selectionData.componentOptions, "Select a package", @@ -143,7 +144,8 @@ fun MetricSelection( "Select an interval for aggregation", modifier = Modifier.weight(1f).padding(end = 0.dp), optionSelected = { timeframeSelected(it) }, - selectionData.selectedTimeframe?.displayName ?: "Please select...", + selectedOption = + selectionData.selectedTimeframe?.displayName ?: "Please select...", ) } ?: Spacer(Modifier.weight(1f)) } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationViewModel.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationViewModel.kt index 654db008..85938b06 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationViewModel.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/VisualizationViewModel.kt @@ -15,13 +15,14 @@ import de.amosproj3.ziofa.ui.shared.toUIOptionsForPids import de.amosproj3.ziofa.ui.visualization.data.DropdownOption import de.amosproj3.ziofa.ui.visualization.data.GraphedData import de.amosproj3.ziofa.ui.visualization.data.SelectionData +import de.amosproj3.ziofa.ui.visualization.data.VisualizationDisplayMode import de.amosproj3.ziofa.ui.visualization.data.VisualizationMetaData import de.amosproj3.ziofa.ui.visualization.data.VisualizationScreenState import de.amosproj3.ziofa.ui.visualization.utils.DEFAULT_SELECTION_DATA import de.amosproj3.ziofa.ui.visualization.utils.DEFAULT_TIMEFRAME_OPTIONS -import de.amosproj3.ziofa.ui.visualization.utils.VisualizationDisplayMode import de.amosproj3.ziofa.ui.visualization.utils.getChartMetadata import de.amosproj3.ziofa.ui.visualization.utils.getPIDsOrNull +import de.amosproj3.ziofa.ui.visualization.utils.isValidSelection import de.amosproj3.ziofa.ui.visualization.utils.toBucketedHistogram import de.amosproj3.ziofa.ui.visualization.utils.toEventList import de.amosproj3.ziofa.ui.visualization.utils.toMovingAverage @@ -96,12 +97,7 @@ class VisualizationViewModel( combine(selectionData, displayMode) { a, b -> a to b } .flatMapLatest { (selection, mode) -> Timber.i("Data flow changed!") - if ( - selection.selectedMetric != null && - selection.selectedMetric is DropdownOption.MetricOption && - selection.selectedTimeframe != null && - selection.selectedTimeframe is DropdownOption.TimeframeOption - ) { + if (isValidSelection(selection.selectedMetric, selection.selectedTimeframe)) { getDisplayedData( selectedComponent = selection.selectedComponent, selectedMetric = selection.selectedMetric, @@ -131,21 +127,15 @@ class VisualizationViewModel( /** Called when a metric is selected */ fun metricSelected(metricOption: DropdownOption) { Timber.i("metricSelected()") - if (metricOption is DropdownOption.MetricOption) { - selectedMetric.value = metricOption - } else { - throw IllegalArgumentException("Wrong usage of this method") - } + require(metricOption is DropdownOption.MetricOption) + selectedMetric.value = metricOption } /** Called when a timeframe is selected */ fun timeframeSelected(timeframeOption: DropdownOption) { Timber.i("timeframeSelected()") - if (timeframeOption is DropdownOption.TimeframeOption) { - selectedTimeframe.value = timeframeOption - } else { - throw IllegalArgumentException("Wrong usage of this method") - } + require(timeframeOption is DropdownOption.TimeframeOption) + selectedTimeframe.value = timeframeOption } fun switchMode() { diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/EventList.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/EventList.kt index 1ab4edc5..0ba5cd85 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/EventList.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/EventList.kt @@ -15,11 +15,11 @@ import androidx.compose.ui.text.intl.Locale import de.amosproj3.ziofa.api.events.BackendEvent @Composable -fun EventList(events: List) { +fun EventList(events: List, modifier: Modifier = Modifier) { val locale = Locale.current.platformLocale events.getOrNull(0)?.let { Header(it) } - LazyColumn(Modifier.fillMaxSize()) { + LazyColumn(modifier.fillMaxSize()) { items(events) { event -> Row { Text(text = event.processId.toString(), modifier = Modifier.weight(1f)) @@ -50,8 +50,8 @@ fun EventList(events: List) { } @Composable -fun Header(firstEvent: BackendEvent) { - Row { +fun Header(firstEvent: BackendEvent, modifier: Modifier = Modifier) { + Row(modifier) { Text(text = "Process ID", modifier = Modifier.weight(1f)) Text(text = "File Descriptor", modifier = Modifier.weight(1f)) Text(text = "Event time since Boot in s", modifier = Modifier.weight(1f)) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/MetricDropdown.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/MetricDropdown.kt index 0286d69c..63b63ff7 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/MetricDropdown.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/MetricDropdown.kt @@ -28,7 +28,7 @@ import de.amosproj3.ziofa.ui.visualization.data.DropdownOption @OptIn(ExperimentalMaterial3Api::class) @Composable fun MetricDropdown( - options: List, // TODO replace with data class + options: List, title: String, modifier: Modifier = Modifier, optionSelected: (DropdownOption) -> Unit, @@ -37,11 +37,7 @@ fun MetricDropdown( var expanded by remember { mutableStateOf(false) } Box(modifier = modifier) { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = it }, - modifier = modifier, - ) { + ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) { TextField( value = selectedOption, onValueChange = {}, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/SwitchModeFab.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/SwitchModeFab.kt index 1e22dcc5..ee34643f 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/SwitchModeFab.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/SwitchModeFab.kt @@ -13,13 +13,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import de.amosproj3.ziofa.ui.visualization.utils.VisualizationDisplayMode +import de.amosproj3.ziofa.ui.visualization.data.VisualizationDisplayMode @Composable fun SwitchModeFab( - modifier: Modifier = Modifier, - onClick: () -> Unit = {}, activeDisplayMode: VisualizationDisplayMode, + onClick: () -> Unit, + modifier: Modifier = Modifier, ) { ExtendedFloatingActionButton( modifier = modifier.padding(end = 25.dp, bottom = 25.dp), diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoBarChart.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoBarChart.kt index fdd87cf0..15f090a7 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoBarChart.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoBarChart.kt @@ -36,15 +36,16 @@ import com.patrykandpatrick.vico.core.cartesian.data.columnSeries import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer import com.patrykandpatrick.vico.core.common.shape.CorneredShape import de.amosproj3.ziofa.ui.visualization.data.VisualizationMetaData +import de.amosproj3.ziofa.ui.visualization.utils.VICO_LINE_COLOR import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber @Composable fun VicoBar( - modifier: Modifier = Modifier, - seriesData: List>, chartMetadata: VisualizationMetaData, + seriesData: List>, + modifier: Modifier = Modifier, ) { Column( modifier.padding(10.dp).fillMaxSize(), @@ -55,9 +56,8 @@ fun VicoBar( Timber.e("bar data $seriesData") modelProducer.SeriesUpdate(seriesData.map { it.second.toInt() }) modelProducer.TimeSeriesChart( - modifier, - chartMetadata, - seriesData.map { it.first.toString() }, + chartMetadata = chartMetadata, + xLabels = seriesData.map { it.first.toString() }, ) } } @@ -65,9 +65,9 @@ fun VicoBar( @Composable private fun CartesianChartModelProducer.TimeSeriesChart( - modifier: Modifier, chartMetadata: VisualizationMetaData, xLabels: List, + modifier: Modifier = Modifier, ) { CartesianChartHost( chart = @@ -76,7 +76,7 @@ private fun CartesianChartModelProducer.TimeSeriesChart( ColumnCartesianLayer.ColumnProvider.series( xLabels.map { _ -> rememberLineComponent( - fill = fill(Color(0xff6438a7)), + fill = fill(VICO_LINE_COLOR), shape = CorneredShape.rounded( bottomLeftPercent = 40, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoTimeSeries.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoTimeSeries.kt index 92ee37a2..19f4209c 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoTimeSeries.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/composables/VicoTimeSeries.kt @@ -36,15 +36,16 @@ import com.patrykandpatrick.vico.core.cartesian.data.lineSeries import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.common.shape.CorneredShape import de.amosproj3.ziofa.ui.visualization.data.VisualizationMetaData +import de.amosproj3.ziofa.ui.visualization.utils.VICO_LINE_COLOR import de.amosproj3.ziofa.ui.visualization.utils.isDefaultSeries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @Composable fun VicoTimeSeries( - modifier: Modifier = Modifier, seriesData: List>, chartMetadata: VisualizationMetaData, + modifier: Modifier = Modifier, ) { Column( modifier.padding(10.dp).fillMaxSize(), @@ -53,15 +54,15 @@ fun VicoTimeSeries( val modelProducer = remember { CartesianChartModelProducer() } if (seriesData.isNotEmpty() && !seriesData.isDefaultSeries()) { modelProducer.SeriesUpdate(seriesData) - modelProducer.TimeSeriesChart(modifier, chartMetadata) + modelProducer.TimeSeriesChart(chartMetadata) } } } @Composable private fun CartesianChartModelProducer.TimeSeriesChart( - modifier: Modifier, chartMetadata: VisualizationMetaData, + modifier: Modifier = Modifier, ) { CartesianChartHost( chart = @@ -69,7 +70,7 @@ private fun CartesianChartModelProducer.TimeSeriesChart( rememberLineCartesianLayer( LineCartesianLayer.LineProvider.series( LineCartesianLayer.rememberLine( - remember { LineCartesianLayer.LineFill.single(fill(Color(0xffa485e0))) } + remember { LineCartesianLayer.LineFill.single(fill(VICO_LINE_COLOR)) } ) ) ), diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationDisplayMode.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationDisplayMode.kt new file mode 100644 index 00000000..d45f85ea --- /dev/null +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationDisplayMode.kt @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2024 Luca Bretting +// +// SPDX-License-Identifier: MIT + +package de.amosproj3.ziofa.ui.visualization.data + +enum class VisualizationDisplayMode { + CHART, + EVENTS, +} diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationScreenState.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationScreenState.kt index 10a17ec1..27b9c3bd 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationScreenState.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/data/VisualizationScreenState.kt @@ -4,8 +4,6 @@ package de.amosproj3.ziofa.ui.visualization.data -import de.amosproj3.ziofa.ui.visualization.utils.VisualizationDisplayMode - sealed class VisualizationScreenState { data class MetricSelectionValid( val graphedData: GraphedData, diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/Constants.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/Constants.kt index 2dc003dc..c76fe480 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/Constants.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/Constants.kt @@ -4,6 +4,7 @@ package de.amosproj3.ziofa.ui.visualization.utils +import androidx.compose.ui.graphics.Color import de.amosproj3.ziofa.ui.configuration.data.BackendFeatureOptions import de.amosproj3.ziofa.ui.visualization.data.DropdownOption import de.amosproj3.ziofa.ui.visualization.data.GraphedData @@ -12,11 +13,7 @@ import de.amosproj3.ziofa.ui.visualization.data.VisualizationMetaData import kotlin.time.DurationUnit import timber.log.Timber -enum class VisualizationDisplayMode { - CHART, - EVENTS, -} - +@Suppress("MagicNumber") // these are constants already val DEFAULT_TIMEFRAME_OPTIONS = listOf( DropdownOption.TimeframeOption(500, DurationUnit.MILLISECONDS), @@ -58,3 +55,6 @@ fun DropdownOption.MetricOption.getChartMetadata(): VisualizationMetaData { } } } + +const val LIGHT_PURPLE = 0xffa485e0 +val VICO_LINE_COLOR = Color(LIGHT_PURPLE) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/VisualizationHelpers.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/VisualizationHelpers.kt index 6149d2e3..86045ca0 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/VisualizationHelpers.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/visualization/utils/VisualizationHelpers.kt @@ -7,13 +7,15 @@ package de.amosproj3.ziofa.ui.visualization.utils import de.amosproj3.ziofa.api.processes.RunningComponent import de.amosproj3.ziofa.ui.shared.toReadableString import de.amosproj3.ziofa.ui.visualization.data.DropdownOption +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract fun DropdownOption.getPIDsOrNull(): List? { return when (this) { is DropdownOption.Global -> null is DropdownOption.Process -> listOf(this.pid) is DropdownOption.AppOption -> this.pids - else -> throw IllegalStateException("Invalid filter") + else -> error("Invalid filter") } } @@ -35,3 +37,17 @@ fun List.toUIOptions() = ) } } + +@OptIn(ExperimentalContracts::class) +fun isValidSelection(selectedMetric: DropdownOption?, selectedTimeframe: DropdownOption?): Boolean { + contract { + returns(true) implies + (selectedMetric is DropdownOption.MetricOption && + selectedTimeframe is DropdownOption.TimeframeOption) + } + + return selectedMetric != null && + selectedMetric is DropdownOption.MetricOption && + selectedTimeframe != null && + selectedTimeframe is DropdownOption.TimeframeOption +}