From 5e1297f63e9c5f8205f73f3bb773e7cd604ce049 Mon Sep 17 00:00:00 2001 From: christianrowlands Date: Fri, 2 Feb 2024 13:08:20 -0500 Subject: [PATCH] Several improvements to the Wi-Fi Spectrum Chart - Pass in the latest wifi scan results so the spectrum chart is not blank by default - Map the SSID to a specific color so it does not keep changing - Remove the padding for the axis labels so that they don't turn into ... on certain resolution screens - Lighten some chart colors to make them easier to see against the black background --- .../fragments/WifiNetworksFragment.java | 29 +++++-- .../fragments/WifiSpectrumFragment.kt | 8 ++ .../ui/wifi/SpectrumChartStyle.kt | 5 -- .../ui/wifi/WifiSpectrumChart.kt | 82 +++++++++++++------ .../ui/wifi/model/WifiNetworkInfoList.kt | 8 ++ .../src/main/res/navigation/wifi.xml | 13 ++- 6 files changed, 104 insertions(+), 41 deletions(-) create mode 100644 networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/model/WifiNetworkInfoList.kt diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiNetworksFragment.java b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiNetworksFragment.java index 7f2ff314..e9e59b66 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiNetworksFragment.java +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiNetworksFragment.java @@ -44,6 +44,7 @@ import com.craxiom.networksurvey.model.WifiNetwork; import com.craxiom.networksurvey.model.WifiRecordWrapper; import com.craxiom.networksurvey.services.NetworkSurveyService; +import com.craxiom.networksurvey.ui.wifi.model.WifiNetworkInfoList; import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; @@ -60,6 +61,7 @@ public class WifiNetworksFragment extends Fragment implements IWifiSurveyRecordL { private FragmentWifiNetworksListBinding binding; private SortedList wifiRecordSortedList; + private final Object wifiRecordSortedListLock = new Object(); private final Handler uiThreadHandler; private WifiViewModel viewModel; @@ -198,7 +200,7 @@ public void onWifiBeaconSurveyRecords(List wifiBeaconRecords) viewModel.incrementScanNumber(); viewModel.setApsInLastScan(wifiBeaconRecords.size()); - synchronized (wifiRecordSortedList) + synchronized (wifiRecordSortedListLock) { wifiRecordSortedList.clear(); wifiRecordSortedList.addAll(wifiBeaconRecords); @@ -255,8 +257,19 @@ public void navigateToWifiSpectrumScreen() FragmentActivity activity = getActivity(); if (activity == null) return; + List wifiNetworks = new ArrayList<>(); + synchronized (wifiRecordSortedListLock) + { + int size = wifiRecordSortedList.size(); + for (int i = 0; i < size; i++) + { + wifiNetworks.add(wifiRecordSortedList.get(i)); + } + } + WifiNetworkInfoList wifiNetworkInfoList = new WifiNetworkInfoList(wifiNetworks); + Navigation.findNavController(activity, getId()) - .navigate(WifiNetworksFragmentDirections.actionWifiListFragmentToWifiSpectrumFragment()); + .navigate(WifiNetworksFragmentDirections.actionWifiListFragmentToWifiSpectrumFragment(wifiNetworkInfoList)); } /** @@ -345,7 +358,7 @@ private void showSortByDialog() */ private void onSortByChanged(SharedPreferences preferences, int selectedIndex) { - synchronized (wifiRecordSortedList) + synchronized (wifiRecordSortedListLock) { preferences.edit().putInt(NetworkSurveyConstants.PROPERTY_WIFI_NETWORKS_SORT_ORDER, selectedIndex).apply(); viewModel.setSortByIndex(selectedIndex); @@ -393,14 +406,14 @@ public void onReceive(Context context, Intent intent) //noinspection SwitchStatementWithoutDefaultBranch switch (state) { - case WifiManager.WIFI_STATE_DISABLED: - viewModel.setScanStatusId(R.string.wifi_scan_status_disabled); - break; - case WifiManager.WIFI_STATE_ENABLED: + case WifiManager.WIFI_STATE_DISABLED -> + viewModel.setScanStatusId(R.string.wifi_scan_status_disabled); + case WifiManager.WIFI_STATE_ENABLED -> + { //noinspection ConstantConditions viewModel.setScanStatusId(viewModel.areUpdatesPaused().getValue() ? R.string.scan_status_paused : R.string.scan_status_scanning); startAndBindToNetworkSurveyService(); - break; + } } } } diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiSpectrumFragment.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiSpectrumFragment.kt index 005aebba..ebc7922b 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiSpectrumFragment.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/fragments/WifiSpectrumFragment.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceManager import com.craxiom.messaging.wifi.WifiBandwidth import com.craxiom.networksurvey.constants.NetworkSurveyConstants @@ -53,6 +54,9 @@ class WifiSpectrumFragment : AServiceDataFragment(), IWifiSurveyRecordListener { container: ViewGroup?, savedInstanceState: Bundle? ): View { + val args: WifiSpectrumFragmentArgs by navArgs() + val wifiNetworks = args.wifiNetworks + val composeView = ComposeView(requireContext()) composeView.apply { @@ -89,6 +93,10 @@ class WifiSpectrumFragment : AServiceDataFragment(), IWifiSurveyRecordListener { wifiSpectrumFragment = this@WifiSpectrumFragment ) } + + if (wifiNetworks.networks.isNotEmpty()) { + onWifiBeaconSurveyRecords(wifiNetworks.networks.toMutableList()) + } } } diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/SpectrumChartStyle.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/SpectrumChartStyle.kt index 78b0c1fd..5352013c 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/SpectrumChartStyle.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/SpectrumChartStyle.kt @@ -20,10 +20,6 @@ internal fun rememberSpectrumChartStyle( lineLayerColors: List, ): ChartStyle { val isSystemInDarkTheme = isSystemInDarkTheme() - // TODO Use the textComponent for the SSID labels when support is added to the library - /*val textComponent = rememberTextComponent( - color = Color(if (isSystemInDarkTheme) DefaultColors.Dark.axisLabelColor else DefaultColors.Light.axisLabelColor), - )*/ return remember(columnLayerColors, lineLayerColors, isSystemInDarkTheme) { val defaultColors = if (isSystemInDarkTheme) DefaultColors.Dark else DefaultColors.Light @@ -46,7 +42,6 @@ internal fun rememberSpectrumChartStyle( lineLayerColors.map { lineChartColor -> LineCartesianLayer.LineSpec( pointConnector = SpectrumPointConnector(), - //dataLabel = textComponent, thicknessDp = 3f, shader = DynamicShaders.color(lineChartColor), //backgroundShader = null, diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/WifiSpectrumChart.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/WifiSpectrumChart.kt index ad327b0e..92dec3e8 100644 --- a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/WifiSpectrumChart.kt +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/WifiSpectrumChart.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -13,6 +14,7 @@ import com.craxiom.networksurvey.R import com.craxiom.networksurvey.fragments.WifiNetworkInfo import com.craxiom.networksurvey.ui.wifi.model.WIFI_SPECTRUM_MAX import com.craxiom.networksurvey.ui.wifi.model.WIFI_SPECTRUM_MIN +import com.patrykandpatrick.vico.compose.axis.axisLabelComponent import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis import com.patrykandpatrick.vico.compose.chart.CartesianChartHost @@ -22,6 +24,7 @@ import com.patrykandpatrick.vico.compose.chart.rememberCartesianChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec import com.patrykandpatrick.vico.compose.component.rememberShapeComponent import com.patrykandpatrick.vico.compose.component.rememberTextComponent +import com.patrykandpatrick.vico.compose.component.shape.shader.color import com.patrykandpatrick.vico.compose.dimensions.dimensionsOf import com.patrykandpatrick.vico.compose.legend.horizontalLegend import com.patrykandpatrick.vico.compose.legend.legendItem @@ -30,17 +33,19 @@ import com.patrykandpatrick.vico.compose.style.ProvideChartStyle import com.patrykandpatrick.vico.compose.style.currentChartStyle import com.patrykandpatrick.vico.core.axis.AxisItemPlacer import com.patrykandpatrick.vico.core.axis.vertical.VerticalAxis +import com.patrykandpatrick.vico.core.chart.layer.LineCartesianLayer import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout import com.patrykandpatrick.vico.core.chart.values.AxisValueOverrider import com.patrykandpatrick.vico.core.component.shape.Shapes +import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders +import com.patrykandpatrick.vico.core.dimensions.MutableDimensions import com.patrykandpatrick.vico.core.legend.HorizontalLegend import com.patrykandpatrick.vico.core.legend.LegendItem import com.patrykandpatrick.vico.core.model.CartesianChartModelProducer +import kotlin.math.absoluteValue /** - * A chart that shows signal values (e.g. RSSI) over time. - * - * @param viewModel The view model that contains the data to display. + * A chart that shows a view of the Wi-Fi spectrum so the user can see where there is free space. */ @Composable internal fun WifiSpectrumChart( @@ -63,25 +68,42 @@ private fun ComposeChart( everyOtherLabel: Boolean, customLabelValues: List ) { - val decorationList = wifiList.mapIndexed { index, wifiNetwork -> + val decorationList = wifiList.map { wifiNetwork -> SsidLabel( ssid = wifiNetwork.ssid, signalStrength = wifiNetwork.signalStrength, channel = wifiNetwork.centerChannel, rememberTextComponent( - color = chartColors[index % chartColors.size], + color = getColorForSsid(wifiNetwork.ssid), textSize = 10.sp, - padding = axisTitlePadding, margins = bottomAxisTitleMargins, typeface = Typeface.MONOSPACE, ) ) } + val lines: List + if (wifiList.isEmpty()) { + lines = listOf( + LineCartesianLayer.LineSpec( + pointConnector = SpectrumPointConnector(), + thicknessDp = 3f, + shader = remember { DynamicShaders.color(color1) }, + ) + ) + } else { + lines = wifiList.map { wifiNetwork -> + LineCartesianLayer.LineSpec( + pointConnector = SpectrumPointConnector(), + thicknessDp = 3f, + shader = DynamicShaders.color(getColorForSsid(wifiNetwork.ssid)), + ) + } + } ProvideChartStyle(rememberSpectrumChartStyle(chartColors)) { //val defaultLines = currentChartStyle.lineLayer.lines CartesianChartHost( - modifier = Modifier.height(220.dp), + modifier = Modifier.height(210.dp), modelProducer = modelProducer, marker = null,//rememberMarker(""), runInitialAnimation = false, @@ -96,6 +118,7 @@ private fun ComposeChart( maxY = WIFI_SPECTRUM_MAX, minY = WIFI_SPECTRUM_MIN ), + lines = lines //remember(defaultLines) { defaultLines.map { it.copy(backgroundShader = null) } }, ), startAxis = @@ -106,6 +129,10 @@ private fun ComposeChart( bottomAxis = rememberBottomAxis( title = stringResource(R.string.channel), + label = axisLabelComponent( + textSize = 12.sp, + padding = MutableDimensions(1f, 1f) + ), itemPlacer = remember { /*AxisItemPlacer.Horizontal.default( spacing = xSpacing, @@ -118,7 +145,10 @@ private fun ComposeChart( }, titleComponent = rememberTextComponent( - background = rememberShapeComponent(Shapes.pillShape, color2), + background = rememberShapeComponent( + Shapes.pillShape, + colorResource(id = R.color.colorAccent) + ), color = Color.White, padding = axisTitlePadding, margins = bottomAxisTitleMargins, @@ -134,6 +164,16 @@ private fun ComposeChart( } } +/** + * Provided a String SSID, this function will return the same color for that SSID. This is useful + * because the color would be randomly assigned otherwise, which means the color is likely to + * change after every scan, which makes it hard to track the same SSID over time. + */ +fun getColorForSsid(ssid: String): Color { + val index = ssid.hashCode().absoluteValue % chartColors.size + return chartColors[index] +} + @Composable private fun rememberSsidLegend( wifiList: List @@ -202,15 +242,10 @@ private fun rememberLegend() = padding = legendPadding, ) -/*private val color1 = Color(0xffb983ff) -private val color2 = Color(0xff91b1fd) -private val color3 = Color(0xff8fdaff) -private val color4 = Color(0xfffab94d)*/ - -private val color1 = Color(0xff4a148c) -private val color2 = Color(0xff880e4f) -private val color3 = Color(0xffb71c1c) -private val color4 = Color(0xffd50000) +private val color1 = Color(0xFF835DB1) +private val color2 = Color(0xFF852659) +private val color3 = Color(0xFFB42D2D) +private val color4 = Color(0xFFD33838) private val color5 = Color(0xffe65100) private val color6 = Color(0xfff57f17) private val color7 = Color(0xffff6f00) @@ -222,9 +257,9 @@ private val color12 = Color(0xff2e7d32) private val color13 = Color(0xff00695c) private val color14 = Color(0xff004d40) private val color15 = Color(0xff01579b) -private val color16 = Color(0xff0d47a1) -private val color17 = Color(0xff1a237e) -private val color18 = Color(0xff311b92) +private val color16 = Color(0xFF1C50A0) +private val color17 = Color(0xFF464B83) +private val color18 = Color(0xFF63559E) private val chartColors = listOf( color1, @@ -246,10 +281,6 @@ private val chartColors = listOf( color17, color18 ) -private val startAxisLabelVerticalPaddingValue = 2.dp -private val startAxisLabelHorizontalPaddingValue = 8.dp -private val startAxisLabelMarginValue = 4.dp -private val startAxisLabelBackgroundCornerRadius = 4.dp private val legendItemLabelTextSize = 12.sp private val legendItemIconSize = 8.dp private val legendItemIconPaddingValue = 10.dp @@ -264,7 +295,4 @@ private val axisTitlePadding = private val axisTitleMarginValue = 4.dp private val bottomAxisTitleMargins = dimensionsOf(top = axisTitleMarginValue) -private val lineColor = Color(0xFF03A9F4) - -//private val chartColors = listOf(lineColor) private val horizontalLayout = HorizontalLayout.fullWidth() diff --git a/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/model/WifiNetworkInfoList.kt b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/model/WifiNetworkInfoList.kt new file mode 100644 index 00000000..de92d21b --- /dev/null +++ b/networksurvey/src/main/java/com/craxiom/networksurvey/ui/wifi/model/WifiNetworkInfoList.kt @@ -0,0 +1,8 @@ +package com.craxiom.networksurvey.ui.wifi.model + +import com.craxiom.networksurvey.model.WifiRecordWrapper +import java.io.Serializable + +data class WifiNetworkInfoList( + val networks: List, +) : Serializable diff --git a/networksurvey/src/main/res/navigation/wifi.xml b/networksurvey/src/main/res/navigation/wifi.xml index 687cb6ff..c015e7c9 100644 --- a/networksurvey/src/main/res/navigation/wifi.xml +++ b/networksurvey/src/main/res/navigation/wifi.xml @@ -39,7 +39,13 @@ app:destination="@id/wifi_spectrum_fragment" app:launchSingleTop="true" app:popUpTo="@+id/main_wifi_fragment" - app:popUpToInclusive="false" /> + app:popUpToInclusive="false"> + + + + +