Commit Graph

116 Commits

Author SHA1 Message Date
torlando-tech
d75bd2d77f feat: display Columba logo on RNode OLED via external framebuffer
Add support for displaying the Columba constellation logo on RNode's
OLED display when connected. The logo is sent via KISS protocol using
the external framebuffer commands (CMD_FB_EXT, CMD_FB_WRITE).

Changes:
- Add conversion script to render icon to 64x64 monochrome bitmap
- Add columba_logo.py with 512-byte framebuffer data
- Add framebuffer methods to ColumbaRNodeInterface
- Auto-display logo after successful RNode connection
- Enable by default via enable_framebuffer config option

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
358d5dd49c feat: add manual Reconnect button for offline RNode interfaces
Add a Reconnect button to the Interface Management screen that appears
when an RNode interface is enabled but offline. This provides a manual
fallback for users when automatic reconnection attempts are exhausted
or CompanionDeviceManager doesn't detect the device.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
45f4d02482 feat: add automatic RNode reconnection after disconnect
When the RNode disconnects (power cycle, out of range, etc.), the
interface now automatically attempts to reconnect:

- Starts a background reconnection loop on disconnect detection
- Tries to reconnect every 10 seconds, up to 30 attempts (~5 minutes)
- Logs progress: "Reconnection attempt X/30 for RNode..."
- Stops reconnection loop when connection succeeds or interface is stopped

Also fixes CompanionDeviceManager-triggered reconnection:
- initialize_rnode_interface() now checks for existing offline interface
- Calls start() to reconnect instead of failing due to missing config
- Handles case where interface already exists but config was cleared

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
d91287ef66 feat: add automatic RNode reconnection and interface status UI
RNode Auto-Reconnection:
- RNodeCompanionService now triggers reconnection when CompanionDeviceManager
  detects the RNode has reappeared after going out of BLE range
- Add reconnectRNodeInterface() to AIDL interface and ReticulumServiceBinder
- Add thread-safe initialization lock in reticulum_wrapper.py to prevent
  concurrent RNode initialization race conditions
- Use 2-second debounce delay before reconnecting to ensure device stability

Interface Status UI Improvements:
- InterfaceManagementViewModel now polls Reticulum every 3 seconds for
  interface online/offline status
- Update isBleInterface() to include RNode type for proper BLE handling
- Add "Interface Offline" error state to getErrorMessage() for enabled
  interfaces that aren't passing traffic
- Make error badges clickable to show detailed error dialog
- Add InterfaceErrorDialog component for detailed interface issue info
- IdentityScreen: make offline interface rows clickable for troubleshooting

Build & Deploy:
- deploy.sh now supports multiple connected devices, deploying to all of
  them in sequence instead of requiring a single device

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
acb10d8e95 fix: improve RNode BLE connection stability and link establishment
- Set HW_MTU to 500 to prevent RNS from truncating packet data before
  link_id computation, which was causing link establishment failures
- Increase BLE stabilization delay from 0.5s to 1.5s to allow connection
  to fully establish before configuration
- Add retry logic to writes (3 attempts with 0.3s delays) to handle
  transient BLE connection issues
- Add diagnostic logging to writeSync() for easier debugging

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
2d5f6b5f1f feat: add live RSSI updates and improve BLE pairing for RNode
- Add live RSSI updates during BLE scanning (every 3 seconds)
- Read RSSI from active RNode BLE connection via readRemoteRssi()
- Store actual BluetoothDevice from scan for proper BLE bonding
- Fix BlePairingHandler to only auto-confirm "Just Works" pairing
- Add RSSI polling in edit mode for connected RNode devices
- Improve pairing UX with two-phase timeout (5s start, 60s PIN entry)

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
1d4c939f75 style: fix detekt line length issues and update baseline
- Shorten log messages in ColumbaApplication.kt
- Break long strings in RNodeWizardViewModel.kt
- Regenerate detekt baseline for new RNode wizard code

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
d1e5f950f7 feat: add RNodeCompanionService for device presence monitoring
- Add RNodeCompanionService that Android binds when associated RNode devices
  appear/connect, enabling background operation
- Register existing companion device associations on app startup
- Call startObservingDevicePresence() after successful CDM association
- Add REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE permission
- Fix RNode LoRa button color in interface type selector (remove highlight)

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
99ef90ac5d feat: add CompanionDeviceManager integration for RNode association
- Add CDM permissions to manifest for Android 12+ device association
- Implement device association flow in RNodeWizardViewModel with BLE/Classic filters
- Show native Android device picker when selecting RNode in wizard
- Add pending changes flag mechanism so "Apply Changes" button appears after
  RNode wizard saves an interface (fixes button not showing issue)

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
ef947f17cc fix: preserve selected interface type when opening Add dialog
When selecting an interface type from the picker, showAddDialog() was
called before updateConfigState(), causing the type to be reset to
defaults. Swapped the order so the selected type is preserved.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
b17a7c285f fix: improve BLE vs Classic detection for RNode devices
Previously, all bonded devices were marked as Classic, causing BLE
devices that were already paired to be incorrectly classified.

Changes:
- BLE scan runs first to definitively identify BLE devices
- Device types are cached in SharedPreferences for offline devices
- Bonded devices not found in BLE scan use cached type or UNKNOWN
- UNKNOWN devices show warning and allow manual type selection
- Edit mode now always scans to detect correct device type
- Selected device type updates when scan finds correct type

This ensures paired BLE devices are correctly identified even when
they were bonded before the app detected their type.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
f82a7d322f refactor: remove dead RNode code from InterfaceConfigDialog
RNode interfaces are now configured through the dedicated wizard flow
(RNodeWizardScreen), making the RNode-related code in InterfaceConfigDialog
unreachable. Removed:
- RNodeFields composable (169 lines)
- RNodeConnectionModeSelector composable (53 lines)
- RNode entry from interface type selector

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
e1312f93b8 fix: add missing coding rate (CR) to region preset cards
The regional preset cards in the Choose Region step were showing
frequency, bandwidth, SF, and TX power but missing the coding rate.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
a477b11918 fix: country search filter not updating in region selection
The search text field wasn't filtering the country list because
getFilteredCountries() was called inside LazyColumn items block
where Compose wasn't properly tracking the searchQuery dependency.

Moved filtered countries computation to top level with remember()
keyed on searchQuery to ensure recomposition when user types.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
d82c801359 fix: detect and clear stale config apply flag on startup
When applying interface configuration changes, a flag is set to prevent
auto-initialization during the restart. If the process crashes before
clearing this flag, subsequent app starts would skip initialization
indefinitely, leaving the service non-functional.

Now checks the service status when the flag is set. If the service is
SHUTDOWN/ERROR, the flag is considered stale and cleared, allowing
normal initialization to proceed.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
1b87294d67 feat: add RNode setup wizard with device discovery and regional presets
Add a guided 3-step wizard for configuring RNode LoRa interfaces that makes
setup accessible to non-technical users.

## New Features

### Step 1: Device Discovery
- Auto-scan for RNode devices via Bluetooth Classic (bonded) and BLE (NUS UUID)
- Display discovered devices with type badge (Classic/BLE), signal strength, paired status
- In-app Bluetooth pairing support with system pairing dialog
- Manual device entry fallback with Bluetooth type selection
- Edit mode shows current device with option to change

### Step 2: Region Selection
- Searchable country list with 40+ regional presets from Reticulum wiki
- Presets for US, EU (Germany, Belgium, Netherlands, etc.), UK, Australia, Asia
- City-specific presets where regulations differ (e.g., Sydney vs Melbourne)
- Each preset shows frequency, bandwidth, spreading factor, TX power
- Custom mode for advanced users who want manual configuration

### Step 3: Review & Configure
- Device summary with Bluetooth type
- Editable interface name
- Region summary (if preset selected)
- Radio settings always visible: frequency, bandwidth, SF, CR, TX power
- Expandable advanced settings: airtime limits, interface mode
- Full validation with error messages

## Technical Changes

### New Files
- `RNodeRegionalPreset.kt`: Data models for presets, BluetoothType enum, DiscoveredRNode
- `RNodeWizardViewModel.kt`: State management, BLE scanning, pairing, validation
- `RNodeWizardScreen.kt`: Main wizard container with step navigation
- `DeviceDiscoveryStep.kt`: Bluetooth device scanning and selection UI
- `RegionSelectionStep.kt`: Country/preset selection UI
- `ReviewConfigStep.kt`: Configuration review and editing UI

### Modified Files
- `MainActivity.kt`: Add wizard route, hide main nav bar during wizard
- `InterfaceManagementScreen.kt`: Route RNode interfaces to wizard, add type selector

## UX Improvements
- Wizard has its own bottom bar with step indicators and Next/Save button
- Main app navigation bar hidden during wizard for full-screen experience
- 100dp bottom spacer pattern for proper scrolling past navigation elements
- Supports both adding new and editing existing RNode interfaces

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
torlando-tech
2b46964125 feat: add RNode LoRa interface support via Bluetooth
Implements RNode interface support for LoRa communication via paired
Bluetooth RNode devices. Uses a Kotlin Bridge architecture where Kotlin
handles Bluetooth I/O and Python handles the KISS protocol.

- **KotlinRNodeBridge**: Handles Bluetooth Classic (SPP/RFCOMM) and BLE
  (Nordic UART Service) connections to RNode hardware. Manages connection
  lifecycle, data buffering, and provides read/write APIs to Python.

- **ColumbaRNodeInterface**: Python interface implementing KISS protocol
  for RNode communication. Handles frame escaping, command parsing, radio
  configuration, and integrates with RNS Transport layer.

- **UI Components**: Added RNode configuration fields to InterfaceConfigDialog
  including device name selector, connection mode (Classic/BLE), frequency,
  bandwidth, spreading factor, coding rate, and TX power settings.

- Supports both Bluetooth Classic (UUID: 00001101-0000-1000-8000-00805F9B34FB)
  and BLE via Nordic UART Service (UUID: 6e400001-b5a3-f393-e0a9-e50e24dcca9e)
- Thread-safe circular buffer for BLE packet reassembly
- Automatic device discovery from paired devices list
- Connection state management with callbacks

- Full KISS protocol implementation (FEND/FESC escape sequences)
- RNode detection and firmware version validation
- Radio parameter configuration (frequency, bandwidth, SF, CR, TX power)
- Airtime limiting support (short-term and long-term)
- Required RNS Transport interface attributes for compatibility

- set_rnode_bridge() to receive Kotlin bridge reference
- initialize_rnode_interface() called during bridge setup
- RNode interface registered with RNS.Transport.interfaces

1. **Chaquopy ByteArray conversion**: Raw bytes from Kotlin needed explicit
   `bytes()` conversion in Python due to Chaquopy's jarray handling.

2. **KISS frame format**: Initial detection commands were missing FEND
   delimiters, causing RNode to not respond to detection requests.

3. **RNS Transport compatibility**: Required iteratively adding interface
   attributes (bitrate, rxb, txb, mode, mtu, HW_MTU, FIXED_MTU,
   AUTOCONFIGURE_MTU, announce_rate_target, ifac_size, etc.) and methods
   (sent_announce(), received_announce(), process_held_announces(),
   should_ingress_limit()) to satisfy RNS Transport requirements.

4. **Owner inbound routing**: Changed from owner.inbound() to direct
   RNS.Transport.inbound() calls since owner was ReticulumWrapper, not
   Transport.

Successfully tested bidirectional communication:
- Announces sent and received between Columba and Sideband via LoRa
- Links established with ~1.8s RTT over LoRa
- Messages delivered from Columba to Sideband
- Messages received from Sideband (routing to correct identity required)

- python/rnode_interface.py (NEW): KISS protocol and RNode interface
- reticulum/rnode/KotlinRNodeBridge.kt (NEW): Bluetooth bridge
- python/reticulum_wrapper.py: RNode bridge integration
- ReticulumServiceBinder.kt: Bridge initialization in setupBridges()
- InterfaceConfigDialog.kt: RNode UI configuration fields
- InterfaceManagementViewModel.kt: RNode state management
- ReticulumConfig.kt: RNode data model with targetDeviceName, connectionMode

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00
Torlando
485a95e5cc Merge pull request #58 from torlando-tech/bugfix/42-shared-instance
Bugfix/42 shared instance
2025-12-08 21:51:59 -05:00
torlando-tech
3dd6d20ad4 test: add comprehensive unit tests for shared instance functionality (#42)
Add SettingsViewModelTest with 27 tests covering:
- RPC key parsing from Sideband config and raw hex formats
- State transitions for shared instance toggles and banners
- Flow collection from repository
- Service restart triggers

Also fix detekt issues:
- Extract complex condition to local variable in SettingsScreen
- Refactor parseRpcKey to use when expression (reduce return count)
- Add justified suppressions for LongMethod, TooManyFunctions,
  LongParameterList, and SwallowedException where appropriate

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 21:44:13 -05:00
torlando-tech
dd891d51d4 feat: improve RPC key UX with Save button and config parsing (#42)
- Add Save button for RPC key instead of auto-save on every keystroke
- Parse Sideband config format to extract just the hex key
- Allow multi-line paste for config format
- Move "Service will restart" hint under toggle
- Collapse banner by default on app start

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 21:05:16 -05:00
torlando-tech
2dac4740df feat: add shared instance availability monitoring and improved UX (#42)
- Add TCP port probing to detect shared instance availability/loss
- Show Snackbar when shared instance becomes available (replaces card)
- Show banner card only when shared instance is relevant
- Reduce loss detection threshold from 30s to 10s
- Probe immediately on startup instead of waiting for poll interval
- Use direct port probing for loss detection (more reliable than networkStatus)

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 20:33:11 -05:00
torlando-tech
eca2e6336c fix: preserve isRestarting state in SettingsViewModel combine flow
The combine flow was creating a new SettingsState without preserving
isRestarting, causing it to reset to false when preferences changed.
This caused the shared instance banner to flicker during service restart.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 17:12:45 -05:00
torlando-tech
d0a84eb399 fix: ensure identity file exists before Python initialization (#42)
Call ensureIdentityFileExists() to recover identity file from database
keyData if missing. Remove silent Python fallback to prevent identity
mismatches that cause message delivery to fail in shared instance mode.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 12:11:18 -05:00
torlando-tech
87b839590c feat: add RPC key configuration for shared instance auth (#42)
Add support for configuring RPC key in shared instance mode to enable
physical layer stats queries (RSSI, SNR, Q) when using another app's
Reticulum instance.

- Add rpcKey field to ReticulumConfig
- Add RPC key input field to SharedInstanceBannerCard
- Store and load RPC key from DataStore via SettingsRepository
- Pass RPC key to Python during initialization

The RPC key can be obtained from Sideband's "Share Instance Access"
settings and pasted into Columba for full stats functionality.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 12:11:10 -05:00
torlando-tech
8c2edf6b98 fix: use patched RNS fork for shared instance RPC fix (#42)
Use forked RNS from torlando-tech/Reticulum@fix-phy-stats-rpc with patch
to catch exceptions in __update_phy_stats(). This prevents crashes when
connecting to another app's shared instance (e.g., Sideband) where RPC
authentication fails due to different identity keys.

Without this patch, RPC AuthenticationError would propagate up and crash
the entire packet processing chain, breaking message delivery.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 12:10:58 -05:00
torlando-tech
20d1bbbfe1 fix: use TCP for shared instance on Android (#42)
Android's app sandboxing prevents Unix domain sockets from working
between different apps. Added `shared_instance_type = tcp` to the
RNS config so Columba can properly connect to Sideband's shared
instance via TCP on port 37428.

Additional changes:
- Replace one-way button with bidirectional toggle for instance mode
- Show banner when using own instance (so user can toggle back)
- Add restart dialog when switching instance modes
- Disable Service Control card when using shared instance
- Disable BLE Connections card when using shared instance

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 23:44:09 -05:00
Torlando
fcae3b4096 Merge pull request #51 from torlando-tech/bugfix/41-appdata-parsing
Bugfix/41 appdata parsing
2025-12-07 19:46:59 -05:00
torlando-tech
9868ec5463 feat: display stamp costs in node details screen
Extract and display stamp cost information from LXMF announces:
- Propagation nodes: show stamp cost with flexibility range and peering cost
- Regular peers: show stamp cost when available

Changes:
- Python: extract stamp costs using LXMF canonical functions
- Add stampCost, stampCostFlexibility, peeringCost to AnnounceEvent model
- Pass stamp costs through PollingManager and ServiceReticulumProtocol
- Add database migration 20->21 for new columns
- Display stamp cost info cards in AnnounceDetailScreen

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 19:23:42 -05:00
torlando-tech
3493a3ba41 fix: pass displayName through service protocol flow (#41)
The previous commit updated PythonReticulumProtocol but missed the
service-based flow. Announces received via ReticulumService now
correctly extract display_name from Python and pass it to AnnounceEvent.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 19:23:42 -05:00
torlando-tech
b037553397 fix: use Python LXMF functions for app_data name parsing (#41)
Offload announce name parsing to Python's canonical LXMF functions:
- LXMF.display_name_from_app_data() for lxmf.delivery and nomadnetwork.node
- LXMF.pn_name_from_app_data() for lxmf.propagation

Pass pre-parsed displayName from Python to Kotlin via AnnounceEvent.
Simplify AppDataParser to just use displayName or generate fallback.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 19:23:42 -05:00
Torlando
4616bc11c8 Merge pull request #47 from torlando-tech/bugfix/38-auto-interface
Bugfix/38 auto interface
2025-12-07 16:40:39 -05:00
Torlando
ba53927413 Merge pull request #50 from torlando-tech/bugfix/39-sideband-server
fix: replace Dublin testnet with Sideband public server
2025-12-07 16:39:29 -05:00
Torlando
9c55574ca7 Merge pull request #48 from torlando-tech/bugfix/contacts-scroll
Bugfix/contacts scroll
2025-12-07 16:39:08 -05:00
torlando-tech
3d063280c9 fix: use real delay in MigrationViewModelTest for IO dispatcher
The test was using virtualized delay inside runTest which doesn't wait
for actual Dispatchers.IO work. Changed to withContext(Dispatchers.Default)
for a real delay and added while loop to properly skip intermediate states.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:34:00 -05:00
torlando-tech
6899df5875 fix: replace Dublin testnet with Sideband public server
Fixes #39

- Replace dublin.connect.reticulum.network with sideband.connect.reticulum.network
- Remove BetweenTheBorders testnet interface
- Add MIGRATION_2_3 for existing users to migrate their interface config
- Update InterfaceDatabase version 2 β†’ 3

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:20:42 -05:00
torlando-tech
6cecf39303 feat: implement backend shared instance detection (#42)
Add backend infrastructure to detect and connect to shared Reticulum
instances (e.g., from Sideband running on the same device).

Changes:
- ReticulumConfig: Add preferOwnInstance field
- reticulum_wrapper.py: Add _check_shared_instance_available() to detect
  TCP shared instances on port 37428, update _create_config_file() to
  support shared instance mode, return is_shared_instance in initialize()
- ServiceReticulumProtocol: Parse is_shared_instance from result and
  save to SettingsRepository
- PythonWrapperManager: Parse is_shared_instance and pass to callback
- ReticulumServiceBinder: Include is_shared_instance in callback JSON
- InterfaceConfigManager: Pass preferOwnInstance to ReticulumConfig
- ColumbaApplication: Load and pass preferOwnInstance preference

Flow:
1. User preference preferOwnInstance loaded from SettingsRepository
2. If false, Python checks for TCP connection to 127.0.0.1:37428
3. If shared instance found, config uses share_instance=yes mode
4. is_shared_instance result saved to SettingsRepository
5. UI reacts via SettingsViewModel reading from repository

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 15:57:09 -05:00
torlando-tech
26cc70c471 feat: add UI for shared Reticulum instance mode (#42)
Add settings UI to inform users when Columba is connected to a shared
RNS instance (e.g., from Sideband) and handle interface management
restrictions.

Changes:
- Add SharedInstanceBannerCard: collapsible banner explaining shared mode
- Update NetworkCard: disable "Manage Interfaces" when using shared instance
- Add preferOwnInstance and isSharedInstance preferences to SettingsRepository
- Wire up SettingsViewModel to read shared instance state from repository

The UI is fully reactive - when isSharedInstance is set to true via
SettingsRepository, the banner appears and interface management is disabled.
Backend integration for detecting/connecting to shared instances will be
implemented in a follow-up PR.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 15:47:16 -05:00
torlando-tech
ab6817bf10 fix: use real delay in MigrationViewModelTest for IO dispatcher
The test was using virtualized delay inside runTest which doesn't wait
for actual Dispatchers.IO work. Changed to withContext(Dispatchers.Default)
for a real delay and added while loop to properly skip intermediate states.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 12:33:18 -05:00
torlando-tech
e70f1829b1 test: add unit tests for nullable AutoInterface ports
- Add serialization tests for null port omission in InterfaceConfigExtTest
- Add parsing tests for partial port scenarios in InterfaceRepositoryTest

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:29:41 -05:00
torlando-tech
16a2594256 fix: use RNS default ports for AutoInterface #38
AutoInterface was using incorrect default ports (48555/49555) that
didn't match RNS defaults (29716/42671), preventing peer discovery
on local WiFi/Ethernet.

Made discovery_port and data_port nullable/optional. When omitted,
RNS automatically uses its defaults. This is more future-proof than
hardcoding values.

- Changed port types from Int to Int? with null default
- Updated serialization to only write ports when explicitly set
- Updated UI placeholders to show RNS defaults (29716/42671)
- Added detekt suppressions for validation function complexity

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:05:58 -05:00
torlando-tech
69c5d57cd9 fix: increase bottom padding in ContactsScreen for FAB clearance
The LazyColumn content was being obscured by the FloatingActionButton,
preventing users from scrolling to see all contacts.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 00:40:12 -05:00
Torlando
558c518f1b Merge pull request #37 from torlando-tech/feature/cpd-duplicate-detection
feat: add PMD CPD for duplicate code detection
2025-12-06 23:59:08 -05:00
torlando-tech
2cd4040c03 fix: suppress NestedBlockDepth warning and update detekt baseline
Adding networkName/passphrase to TCPClient serialization increased
nesting depth. Added @Suppress annotation and regenerated baseline.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:49:40 -05:00
torlando-tech
0f0bd11f3e fix: add missing networkName/passphrase to PythonReticulumProtocol TCPClient
The TCPClient serialization fix was applied to ServiceReticulumProtocol
but not to PythonReticulumProtocol. This ensures both protocol classes
serialize TCPClient network_name and passphrase fields consistently.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:41:52 -05:00
torlando-tech
aa5ae22a83 fix: suppress detekt LongParameterList and ThrowsCount warnings
- Add @Suppress("LongParameterList") to buildThemeEntity in CustomThemeRepository
  (entity builder naturally requires all fields)
- Add @Suppress("ThrowsCount") to getLxmfDestination and getLxmfIdentity
  (multiple validation checks require distinct error messages)
- Refactor to use checkNotNull() and require() for cleaner validation

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:22:37 -05:00
torlando-tech
8dd324519f fix: increase bottom padding in IdentityManagerScreen for dropdown access
Increased LazyColumn bottom padding from 88dp to 160dp to allow scrolling
far enough to access context menus on the last identity in the list.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:14:39 -05:00
torlando-tech
c6a643cd26 fix: resolve detekt issues for null safety and redundant code
- Replace wrapper!! with checkNotNull() in PythonReticulumProtocol (12 methods)
- Remove redundant suspend modifiers from:
  - BleStatusRepository.getConnectedPeerCount()
  - BleStatusRepository.getConnectedPeers()
  - BleStatusRepository.disconnectPeer()
  - ServiceReticulumProtocol.getLxmfDestination()
  - DebugViewModel.getOrCreateDestination()
- Remove unused properties: expanded, showPermissionRationale
- Fix UnnecessarySafeCall: use takeIf+let pattern for optString()
- Fix UnnecessaryNotNullOperator: use local val capture in InterfaceConfigDialog
- Replace IllegalStateException throw with checkNotNull()

Reduces weighted detekt issues from 47 to 34.

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 22:48:15 -05:00
torlando-tech
176117254b fix: resolve detekt UnsafeCallOnNullableType, UseOrEmpty, and VarCouldBeVal issues
- Replace !! with local val captures for null-checked variables
- Replace ?: "" with .orEmpty() and ?: emptyList() with .orEmpty()
- Change unused var to val in InterfaceManagementScreen and QrScannerScreen

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 22:36:08 -05:00
torlando-tech
eab2559970 fix: remove unused createRepository helper in InterfaceRepositoryTest
πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 22:26:47 -05:00
torlando-tech
e558b03b83 refactor: extract shared components to reduce code duplication (round 4)
- Add parseIdentityResultJson() for JSON-to-Map conversion with tests
- Extract IdentityQrCodeDialogContent for shared QR dialog structure
- Add ContactListItemWithMenu to dedupe pinned/all contacts sections
- Net reduction: 170 lines removed

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 22:16:52 -05:00