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– implementsAppRepositoryand talks to Supabase via Retrofit‑style clients defined inremote/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:
DashboardScreen→DashboardViewModelcallsScanInventoryUseCase.ScanInventoryUseCasereads installed packages fromDeviceInventoryRepoImpl(local inventory source).- Package list is passed to
SupabaseAppRepository.getAlternatives(). - The repository builds HTTP requests using Supabase credentials stored in
app/src/main/assets/supabase.propertiesand maps JSON to Kotlin models (SupabaseModels.kt). - Results are cached by
CacheRepositoryImpl(Room tablesCachedSolution/CachedTarget). - 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.kteach > 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.ymllackstimeout-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.