Skip to content

Commit

Permalink
Several improvements to the Wi-Fi Spectrum Chart
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
christianrowlands committed Feb 2, 2024
1 parent b98cb89 commit 5e1297f
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -60,6 +61,7 @@ public class WifiNetworksFragment extends Fragment implements IWifiSurveyRecordL
{
private FragmentWifiNetworksListBinding binding;
private SortedList<WifiRecordWrapper> wifiRecordSortedList;
private final Object wifiRecordSortedListLock = new Object();
private final Handler uiThreadHandler;

private WifiViewModel viewModel;
Expand Down Expand Up @@ -198,7 +200,7 @@ public void onWifiBeaconSurveyRecords(List<WifiRecordWrapper> wifiBeaconRecords)
viewModel.incrementScanNumber();
viewModel.setApsInLastScan(wifiBeaconRecords.size());

synchronized (wifiRecordSortedList)
synchronized (wifiRecordSortedListLock)
{
wifiRecordSortedList.clear();
wifiRecordSortedList.addAll(wifiBeaconRecords);
Expand Down Expand Up @@ -255,8 +257,19 @@ public void navigateToWifiSpectrumScreen()
FragmentActivity activity = getActivity();
if (activity == null) return;

List<WifiRecordWrapper> 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));
}

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -89,6 +93,10 @@ class WifiSpectrumFragment : AServiceDataFragment(), IWifiSurveyRecordListener {
wifiSpectrumFragment = this@WifiSpectrumFragment
)
}

if (wifiNetworks.networks.isNotEmpty()) {
onWifiBeaconSurveyRecords(wifiNetworks.networks.toMutableList())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ internal fun rememberSpectrumChartStyle(
lineLayerColors: List<Color>,
): 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
Expand All @@ -46,7 +42,6 @@ internal fun rememberSpectrumChartStyle(
lineLayerColors.map { lineChartColor ->
LineCartesianLayer.LineSpec(
pointConnector = SpectrumPointConnector(),
//dataLabel = textComponent,
thicknessDp = 3f,
shader = DynamicShaders.color(lineChartColor),
//backgroundShader = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ 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
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
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -63,25 +68,42 @@ private fun ComposeChart(
everyOtherLabel: Boolean,
customLabelValues: List<Float>
) {
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<LineCartesianLayer.LineSpec>
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,
Expand All @@ -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 =
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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<WifiNetworkInfo>
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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()
Original file line number Diff line number Diff line change
@@ -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<WifiRecordWrapper>,
) : Serializable
13 changes: 12 additions & 1 deletion networksurvey/src/main/res/navigation/wifi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

<argument
android:name="wifiNetworks"
app:argType="com.craxiom.networksurvey.ui.wifi.model.WifiNetworkInfoList"
app:nullable="false" />
</action>
</fragment>

<fragment
Expand All @@ -65,6 +71,11 @@
android:name="com.craxiom.networksurvey.fragments.WifiSpectrumFragment"
android:label="Wi-Fi Spectrum">

<argument
android:name="wifiNetworks"
app:argType="com.craxiom.networksurvey.ui.wifi.model.WifiNetworkInfoList"
app:nullable="false" />

<action
android:id="@+id/action_wifi_details_to_settings"
app:destination="@id/settings_fragment"
Expand Down

0 comments on commit 5e1297f

Please sign in to comment.