Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/anfegomezver/spectrum24ghz/llms.txt

Use this file to discover all available pages before exploring further.

Spectrum 2.4GHz is built around a single-Activity architecture with no Fragments and no ViewModel layer. All UI state — the currently selected tab, the list of scanned networks, scan history, and permission state — is managed directly inside MainActivity. Custom Canvas-based views handle all data visualisation, and two RecyclerView adapters power the scrollable lists. This flat, self-contained structure keeps the codebase easy to navigate while satisfying the app’s real-time Wi-Fi scanning requirements.

Package Structure

The source code is organized into two packages under app/src/main/java/:
PackageContents
com.spectrum24ghzMainActivity, NetworkAdapter, ChannelListAdapter, TimeGraphView, StatisticsGraphView
com.spectrum24ghz.modelsScannedNetwork, WifiChannel

Class Breakdown

MainActivity

Orchestrates the entire app: tab navigation, permission requests, scan lifecycle, adapter wiring, and dialog presentation.

ScannedNetwork

Immutable model representing a single detected access point, including SSID, BSSID, RSSI, signal quality, frequency, channel, and capabilities.

WifiChannel

Represents a single 2.4 GHz channel (1–13). Holds frequency, region label, prime-channel flag, and a mutable list of networks found on that channel.

NetworkAdapter

RecyclerView adapter that displays the flat network list with signal icons, RSSI colour coding, and a click listener for detail dialogs.

ChannelListAdapter

RecyclerView adapter that renders each channel row with its network count and colour-coded saturation indicator.

TimeGraphView

Custom View that draws a multi-line RSSI history graph on Canvas using Paint, Path, and CornerPathEffect.

StatisticsGraphView

Custom View that renders horizontal bar charts (signal quality, security type) and a vertical histogram (channel distribution) directly on Canvas.

MainActivity

MainActivity is the single entry point and lifecycle owner. On onCreate it:
  • Inflates the layout via View Binding (ActivityMainBinding.inflate)
  • Obtains a WifiManager reference from the application context
  • Wires NetworkAdapter to rvChannels and ChannelListAdapter to rvChannelsList
  • Registers click listeners on the four tab buttons (Networks, Channels, Time Graph, Statistics)
  • Starts an auto-update Handler that triggers initiateWifiScan() every 5 seconds when the Time Graph tab is active
  • Immediately calls requestPermissionsAndScan() to kick off the first scan
Tab selection is handled by selectTab(int tabIndex):
IndexTabVisible view
0NetworksrvChannels (network list)
1ChannelsrvChannelsList (channel list)
2Time GraphtimeGraph (TimeGraphView)
3StatisticsscrollStats (StatisticsGraphView)

ScannedNetwork

ScannedNetwork is a fully immutable data class. All seven fields are set in the constructor and exposed via getters only:
public ScannedNetwork(String ssid, String bssid, int rssi, int signalPercent,
                      int frequency, int channel, String capabilities)
FieldTypeDescription
ssidStringNetwork name (or "<oculto>" if hidden)
bssidStringMAC address of the access point
rssiintRaw signal level in dBm
signalPercentintNormalised signal strength 0–100 %
frequencyintFrequency in MHz (e.g. 2437)
channelint2.4 GHz channel number (1–13, or 0 if unknown)
capabilitiesStringRaw capabilities string from ScanResult
The getSecurityLabel() method derives a human-readable security type by inspecting the capabilities string for keywords in priority order: WPA3WPA2WPAWEPOWEOpen (No Security). It also distinguishes Enterprise (EAP) from Personal (PSK) variants.

WifiChannel

WifiChannel represents one of the thirteen 2.4 GHz channels tracked by the app (channels 1–13, built by buildAllChannels() in MainActivity). Channels 1, 6, and 11 are flagged as prime (isPrime = true) — the three non-overlapping channels in the 2.4 GHz band.
public WifiChannel(int channel, int frequencyMhz, String regionLabel,
                   boolean isRestricted, boolean isPrime)
The networks list inside each WifiChannel is mutable and rebuilt on every scan cycle. The only mutable field exposed via a setter is isExpanded (used by the adapter to track expand/collapse UI state).

NetworkAdapter

NetworkAdapter extends RecyclerView.Adapter<NetworkAdapter.NetworkViewHolder>. It holds a reference to the shared scannedNetworks list in MainActivity and binds each item using ItemNetworkBinding (View Binding). The OnNetworkClickListener interface allows MainActivity to handle item taps and display a detail AlertDialog:
public interface OnNetworkClickListener {
    void onNetworkClick(ScannedNetwork network);
}
Signal strength is displayed with a tiered icon and colour:
Signal percentIconColour token
≥ 85 %ic_wifi_4sig_strong
60–84 %ic_wifi_3sig_good
35–59 %ic_wifi_2sig_medium
15–34 %ic_wifi_1sig_medium
< 15 %ic_wifi_0sig_weak

ChannelListAdapter

ChannelListAdapter extends RecyclerView.Adapter<ChannelListAdapter.ChannelViewHolder> and uses ItemChannelListBinding. The OnChannelClickListener interface delegates tap events to MainActivity for a channel-detail dialog:
public interface OnChannelClickListener {
    void onChannelClick(WifiChannel channel);
}
Saturation is estimated as min(networkCount × 25, 100)% and colour-coded green (≤ 30 %), yellow/orange (31–60 %), or red (> 60 %).

TimeGraphView

TimeGraphView extends android.view.View and overrides onDraw(Canvas). It receives the full scanHistory list from MainActivity via updateHistory() (which calls invalidate() to trigger a redraw). Each draw pass:
  • Renders a dashed grid from −100 dBm to −20 dBm on the Y-axis in 10 dBm steps
  • Displays a sliding window of the last 10 scan cycles on the X-axis
  • Draws one Path per unique BSSID using CornerPathEffect(25) for smooth curves
  • Adjusts line opacity and stroke width based on the most recent RSSI value (strong: full opacity at 5 px; medium: 150 alpha at 3.5 px; weak: 70 alpha at 2.5 px)
  • Renders a colour-coded legend overlay (up to 6 networks) in the upper-left corner
Paint objects are initialised once in init() and reused across draw calls to avoid object allocation during rendering.

StatisticsGraphView

StatisticsGraphView extends android.view.View and overrides onDraw(Canvas) to render three chart sections in a single vertically scrolling canvas (fixed measured height: 2200 px):
  1. Signal Quality — horizontal bar chart with four quality bands: Excellent (≥ 85 %), Good (60–84 %), Medium (35–59 %), Weak (< 35 %)
  2. Channel Usage — vertical histogram for channels 1–14; channels 1, 6, and 11 are highlighted in sig_strong (green) to indicate non-overlapping prime channels
  3. Security Types — horizontal bars broken down by security label returned from ScannedNetwork.getSecurityLabel()
Data is pushed in by MainActivity.updateStatisticsView() which calls statsGraph.updateData(qCounts, cCounts, sCounts).

Scan Lifecycle

The following sequence describes one complete scan cycle from user interaction to UI update:
1

requestPermissionsAndScan()

Checks whether ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, and (on API 33+) NEARBY_WIFI_DEVICES are granted. If any are missing, permissionLauncher prompts the user. On grant, calls startScanFlow().
2

startScanFlow()

Enforces a 30-second cooldown (SCAN_COOLDOWN_MS = 30 000). If a scan was started less than 30 seconds ago, the cached WifiManager.getScanResults() are loaded immediately via loadCachedResults() without triggering a new hardware scan.
3

initiateWifiScan()

Records lastScanStartMs (via SystemClock.elapsedRealtime()), clears previous results, registers scanReceiver as a BroadcastReceiver for SCAN_RESULTS_AVAILABLE_ACTION, calls wifiManager.startScan(), and posts a 12-second timeout (SCAN_TIMEOUT_MS = 12 000) on scanTimeoutHandler.
4

BroadcastReceiver.onReceive() or timeout fires

Whichever arrives first — the system broadcast or the 12-second timeout — calls handleScanResults(boolean fresh). The receiver is immediately unregistered and the timeout is cancelled to prevent double processing.
5

handleScanResults()

Calls populateNetworks() to rebuild scannedNetworks and populate channel buckets, then notifies both adapters. The snapshot is appended to scanHistory (capped at 20 cycles). Both timeGraph and (if the Statistics tab is active) statsGraph are refreshed.
6

populateNetworks()

Calls wifiManager.getScanResults() and filters results to the 2.4 GHz band using FREQ_MIN = 2410 and FREQ_MAX = 2486. For each result, constructs a ScannedNetwork, maps it to the correct WifiChannel, and sorts both lists by descending RSSI.

Key Constants

ConstantValuePurpose
SCAN_COOLDOWN_MS30 000 msMinimum interval between hardware scans
SCAN_TIMEOUT_MS12 000 msMaximum wait time for scan results broadcast
FREQ_MIN2410 MHzLower frequency bound for 2.4 GHz band filter
FREQ_MAX2486 MHzUpper frequency bound for 2.4 GHz band filter
Max history size20 cyclesMaximum entries retained in scanHistory
Sliding window10 scansNumber of cycles shown on the Time Graph at once

View Binding

View Binding is enabled in build.gradle (buildFeatures { viewBinding true }). The build system generates a binding class for every layout file. The app uses two binding classes:
  • ActivityMainBinding — wraps activity_main.xml; used in MainActivity to access all root-level views without any findViewById() calls
  • DialogAboutBinding — wraps dialog_about.xml; inflated on demand when the About dialog is shown
All adapter ViewHolders also use View Binding (ItemNetworkBinding and ItemChannelListBinding) rather than calling itemView.findViewById().

Build docs developers (and LLMs) love