Skip to content

Add APK sharing feature with local HTTP server and QR code#430

Merged
torlando-tech merged 8 commits intomainfrom
claude/add-apk-sharing-WW5OV
Feb 16, 2026
Merged

Add APK sharing feature with local HTTP server and QR code#430
torlando-tech merged 8 commits intomainfrom
claude/add-apk-sharing-WW5OV

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

Adds a new APK sharing feature that allows users to share the Columba messenger app with nearby devices over WiFi. The implementation includes a lightweight HTTP server that serves the APK file and a UI screen with QR code generation for easy discovery.

Key Changes

  • ApkSharingServer: New service that runs a lightweight HTTP server on a random available port

    • Serves the APK file at /columba.apk with proper HTTP headers
    • Provides a simple HTML download page at the root path
    • Automatically detects the device's local WiFi IP address
    • Thread-safe with atomic state management
  • ApkSharingViewModel: Manages the server lifecycle and APK file preparation

    • Copies the APK from the app's source directory to a cache directory
    • Starts the HTTP server and generates the download URL
    • Provides a fallback share intent for Bluetooth/Nearby Share
    • Cleans up resources when the screen is closed
  • ApkSharingScreen: New UI screen for the sharing feature

    • Displays QR code containing the download URL
    • Shows step-by-step instructions for the receiving device
    • Displays APK file size and download URL
    • Provides error messages if WiFi is unavailable
    • Includes alternative sharing options via Android share sheet
  • ShareColumbaCard: New settings card in the Settings screen

    • Provides easy access to the APK sharing feature
    • Collapsible card with description and action button
  • Navigation: Integrated into the main navigation graph

    • Added route apk_sharing to the navigation controller
    • Connected from Settings screen via onNavigateToApkSharing callback
  • FileProvider: Updated file_paths.xml to allow sharing APK files from cache directory

Implementation Details

  • The HTTP server uses coroutines and runs on Dispatchers.IO to avoid blocking the UI
  • APK file is copied to cache on demand, allowing it to be served and shared via FileProvider
  • Local IP detection prioritizes WiFi interfaces but falls back to any available network interface
  • The server gracefully handles client connections and properly closes resources
  • QR code contains the full HTTP URL for direct browser access
  • Supports both WiFi-based QR code sharing and traditional Android share methods

https://claude.ai/code/session_01Vhz4fTDD87Tq6RP5i9tcaR

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 9, 2026

Greptile Summary

Adds a local WiFi-based APK sharing feature that allows users to share the Columba app with nearby devices using a QR code. The implementation addresses all previously identified threading and race condition issues.

Key improvements from review feedback:

  • Fixed concurrent client handling by using a bounded thread pool (MAX_CONCURRENT_CLIENTS = 4) instead of inline blocking calls
  • Eliminated race condition in server readiness check by replacing fixed delay with CompletableDeferred pattern via prepareStart()
  • Added proper duplicate server launch prevention by checking serverJob?.isActive

Implementation highlights:

  • Lightweight HTTP server serves APK at /columba.apk with proper Content-Disposition headers
  • APK filename includes version number (columba-${VERSION_NAME}.apk) for clarity
  • Server binds to random port and detects local WiFi IP address
  • Fallback to Android share sheet (Bluetooth, Nearby Share) when WiFi unavailable
  • Proper resource cleanup in onCleared() - stops server, deletes cached APK
  • Comprehensive test suite with 11 tests covering server lifecycle, concurrent clients, and edge cases

Architecture:

  • ApkSharingServer: Standalone HTTP server with thread pool executor
  • ApkSharingViewModel: Manages server lifecycle and APK preparation
  • ApkSharingScreen: UI with QR code display and instructions
  • FileProvider configured for cache directory APK sharing

Confidence Score: 4/5

  • Safe to merge with minor monitoring recommended for real-world WiFi scenarios
  • All previously identified critical issues (threading, race conditions, duplicate launches) have been properly addressed. The implementation is well-tested with comprehensive unit tests. The bounded thread pool prevents resource exhaustion, and the CompletableDeferred pattern ensures proper synchronization. Score of 4 (not 5) because this is a new feature serving executable files over local network, so monitoring initial rollout is prudent.
  • No files require special attention - all previous review feedback has been addressed

Important Files Changed

Filename Overview
app/src/main/java/com/lxmf/messenger/service/ApkSharingServer.kt New HTTP server implementation with bounded thread pool for handling concurrent clients; previous threading issues addressed
app/src/main/java/com/lxmf/messenger/viewmodel/ApkSharingViewModel.kt Server lifecycle management with fixed race condition using prepareStart() and proper duplicate launch prevention
app/src/main/java/com/lxmf/messenger/ui/screens/ApkSharingScreen.kt UI screen for APK sharing with QR code display and alternative sharing methods; no issues found
app/src/test/java/com/lxmf/messenger/service/ApkSharingServerTest.kt Comprehensive test suite for HTTP server with proper lifecycle management and error cases; well-written tests

Sequence Diagram

sequenceDiagram
    participant User
    participant Settings
    participant VM as ApkSharingViewModel
    participant Server as ApkSharingServer
    participant Client as Receiver Browser
    
    User->>Settings: Tap "Share Columba APK"
    Settings->>VM: Navigate to APK Sharing
    VM->>VM: prepareApkFile()
    VM->>VM: Copy APK to cache
    VM->>Server: prepareStart()
    Server-->>VM: Return CompletableDeferred
    VM->>Server: launch start(apkFile)
    Server->>Server: Bind to port 0 (random)
    Server->>Server: Complete deferred with port
    VM->>VM: await portDeferred
    VM->>VM: Build download URL
    VM->>User: Display QR code
    
    Client->>Client: Scan QR code
    Client->>Server: GET /
    Server-->>Client: HTML download page
    Client->>Server: GET /columba.apk
    Server->>Server: handleClient() in thread pool
    Server-->>Client: APK file with headers
    
    User->>VM: Navigate away
    VM->>Server: stop()
    Server->>Server: Close socket & executor
    VM->>VM: Delete cached APK
Loading

Last reviewed commit: 9a8fc19

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@torlando-tech torlando-tech linked an issue Feb 9, 2026 that may be closed by this pull request
@torlando-tech torlando-tech added this to the v0.9.0 milestone Feb 9, 2026
@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai review

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Feb 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@torlando-tech torlando-tech force-pushed the claude/add-apk-sharing-WW5OV branch from 4d14c63 to 9a8fc19 Compare February 15, 2026 22:35
@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai

claude and others added 6 commits February 15, 2026 19:07
Adds the ability to share the Columba APK directly from within the app.
Two sharing methods are provided:

1. WiFi sharing (QR code): Starts a lightweight HTTP server on the device
   that serves the APK. A QR code with the download URL is displayed.
   The receiver scans it, opens the link in their browser, and downloads
   the APK. Both devices must be on the same WiFi network.

2. Android share sheet: Share the APK file via Bluetooth, Nearby Share,
   or any other installed sharing app. Works fully offline.

New files:
- ApkSharingServer.kt: Minimal HTTP server using ServerSocket
- ApkSharingViewModel.kt: Manages server lifecycle and APK preparation
- ApkSharingScreen.kt: Compose screen with QR code and share button
- ShareColumbaCard.kt: Settings card for accessing the feature

Closes #303

https://claude.ai/code/session_01Vhz4fTDD87Tq6RP5i9tcaR
- Handle clients concurrently: each accepted socket is now dispatched to
  its own thread with a 30s read timeout, so a slow client no longer
  blocks the accept loop from serving other receivers.

- Replace racy delay(100) with CompletableDeferred<Int> (awaitPort):
  the ViewModel now suspends until the server socket is actually bound,
  eliminating false "failed to start" on slower devices.

- Guard against leaking parallel servers: startServer() now checks
  serverJob?.isActive before launching, preventing duplicate server
  instances from rapid recomposition or double-navigation.

- Fix all detekt violations in getLocalIpAddress: refactored into small
  helper functions (findWifiAddress, findAnyAddress, firstIpv4Address,
  isUsableInterface, isWifiInterface) to reduce cyclomatic complexity,
  return count, and loop jump statements.

https://claude.ai/code/session_01Vhz4fTDD87Tq6RP5i9tcaR
…rver

The previous awaitPort()/start() pattern had a race condition where the
ViewModel would await on a stale CompletableDeferred that start() would
then reassign. This commit introduces prepareStart() which returns the
CompletableDeferred before launching, so both caller and server operate
on the same instance. Also fixes detekt LoopWithTooManyJumpStatements
in findWifiAddress().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace unbounded Thread-per-client with a fixed pool of 4 threads to
prevent thread exhaustion when many devices connect simultaneously.
Add 13 unit tests covering server lifecycle, HTTP responses, and edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use BuildConfig.VERSION_NAME in the APK filename (e.g. columba-0.8.0.apk)
- Add downloadFileName property to ApkSharingServer for Content-Disposition
- Fix test hangs: replace Thread.sleep with job.join(), remove withTimeout
  (incompatible with runTest virtual time when awaiting real IO threads)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds 29 new tests covering:
- ApkSharingScreen (15 tests): all UI states (loading, error, server running),
  QR code display, instructions, alternative sharing section
- ShareColumbaCard (6 tests): rendering, expansion, navigation callback
- ApkSharingViewModel (8 tests): state management, error handling,
  createShareIntent, idempotent startServer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@torlando-tech torlando-tech force-pushed the claude/add-apk-sharing-WW5OV branch from 1d431c9 to ccfccf5 Compare February 16, 2026 00:15
torlando-tech and others added 2 commits February 15, 2026 23:50
… suite

The test suite accumulated Robolectric/Compose resources across 5800+
tests in a single JVM, causing PagingData rendering failures in
MessagingScreenTest. Setting forkEvery=100 restarts the JVM periodically.

Also adds missing Dispatchers.resetMain() in ApkSharingViewModelTest
to prevent test dispatcher pollution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Robolectric/Compose resource exhaustion causes PagingData rendering
failures in MessagingScreenTest when 10+ Compose test classes share a
JVM. The new APK sharing tests pushed the suite past this threshold.

- Add test sharding via -PtestShard/-PtestShardTotal Gradle properties
  that distribute test classes across parallel CI jobs
- Replace single kotlin-tests job with 4-shard matrix (fail-fast: false)
- Reduce forkEvery from 100 to 10 classes: sharding concentrates
  Compose test classes (modulo distribution groups alphabetically
  adjacent UI tests), requiring more frequent JVM restarts
- Remove Robolectric from ApkSharingServerTest (pure Java networking)
  and add proper coroutine teardown to prevent thread leaks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@torlando-tech torlando-tech merged commit 803e622 into main Feb 16, 2026
13 checks passed
@torlando-tech torlando-tech deleted the claude/add-apk-sharing-WW5OV branch February 16, 2026 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Share Columba APK from Columba

2 participants