The Problem

Many Android users rely on proprietary apps without knowing free‑software alternatives. Enterprises that enforce open‑source policies need a way to discover such apps on a device and suggest replacements, but there is no lightweight, on‑device scanner that works without sending package data to a closed backend.

What This Does

LibreFind is an Android‑only client that scans the locally installed packages, matches them against a community‑maintained Supabase database, and shows FOSS alternatives together with a “sovereignty score”. The UI lives under app/src/main/java/com/jksalcedo/librefind/ui/…, while data handling is in app/src/main/java/com/jksalcedo/librefind/data/. Core entry points include:

  • MainActivity.kt – launches the Compose UI and injects the Koin graph (di/KoinModule.kt).
  • SupabaseAppRepository.kt – implements AppRepository and talks to Supabase via Retrofit‑style clients defined in remote/model/SupabaseModels.kt.
  • AppDatabase.kt + DAO files (IgnoredAppDao.kt, ReclassifiedAppDao.kt, etc.) – provide Room persistence for inventory, user selections and cache.

How It Is Wired

Execution starts when Android creates com.jksalcedo.librefind.MainActivity. In onCreate it calls setContent { LibreFindApp() } (see LibreFindApp.kt). The composable builds the navigation graph (ui/navigation/NavGraph.kt) and injects ViewModels via Koin (di/SupabaseModule.kt supplies SupabaseAppRepository).

Typical scan flow:

  1. DashboardScreen → DashboardViewModel calls ScanInventoryUseCase.
  2. ScanInventoryUseCase reads installed packages from DeviceInventoryRepoImpl (local inventory source).
  3. Package list is passed to SupabaseAppRepository.getAlternatives().
  4. The repository builds HTTP requests using Supabase credentials stored in app/src/main/assets/supabase.properties and maps JSON to Kotlin models (SupabaseModels.kt).
  5. Results are cached by CacheRepositoryImpl (Room tables CachedSolution / CachedTarget).
  6. UI composables (AlternativeDetailScreen, ReportScreen, etc.) consume the LiveData/StateFlow exposed by the ViewModels.

The widest blast radius lies in SupabaseAppRepository.kt (≈1 300 lines) and SubmitScreen.kt (≈1 300 lines); changes here affect most UI paths and remote interactions. No circular module dependencies were detected, and the import graph contains 85 Kotlin files with zero internal edges, indicating a flat package structure.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/librefind
cd librefind

# Build the Android app (requires JDK 11+ and Android SDK)
./gradlew assembleDebug

Configuration: place a supabase.properties file in app/src/main/assets/ containing the Supabase URL and anon key (referenced by SupabaseAppRepository). No other environment variables are required.

Run: install the generated APK (app/build/outputs/apk/debug/app-debug.apk) on an Android device or emulator. The app launches via MainActivity.

Real‑World Use

A corporate device management tool could embed LibreFind as a side‑load. After installation, a scheduled job triggers the scan UI, collects the list of proprietary packages, and automatically pushes a CSV of recommended FOSS replacements to the device’s MDM server via the existing SubmitProposalUseCase.

val csv = inventory.map { "${it.packageName},${it.alternative?.packageName ?: "none"}" }
mdmClient.upload(csv)

Code Health & Issues

Measured static analysis (43 findings)

  • High – Cognitive load: deep nesting (max depth 12) in MainActivity.kt, FieldWithHelp.kt, TutorialOverlay.kt.
  • High – Duplicated code: identical 6‑line blocks across DAO and repository files (e.g., IgnoredAppDao.kt, ReclassifiedAppDao.kt).
  • High – Oversized files: SubmitScreen.kt, SupabaseAppRepository.kt, AlternativeDetailScreen.kt each > 1 300 lines.
  • Medium – High branching density: 19 branches in 61 lines in InventorySource.kt, SupabaseModels.kt, Routes.kt.

Code‑health audit

  • HIGH – GitHub Actions use tags (@v4, @v2); pin to commit SHA to avoid supply‑chain risk.
  • MEDIUM – Test coverage is low (2 test files vs. 88 source files). Add unit/instrumentation tests for high‑fan‑in modules.
  • LOW – Workflow codeql.yml lacks timeout-minutes; set explicit limits to prevent overlapping runs.

All other hygiene items (CI present, license, no committed secrets) are satisfactory.

The Bottom Line

LibreFind provides a functional on‑device scanner and recommendation engine, with a clear separation between UI, domain, and data layers. However, the codebase suffers from deep nesting, duplicated DAO logic, and several very large files, which increase maintenance risk. It is suitable for teams willing to refactor the hot spots and expand test coverage before using it in production environments.