- Kotlin 100%
- Load site favicons (svg → png → ico fallback) via Coil 3 with SubcomposeAsyncImage; globe icon as final fallback - Register SvgDecoder in ImplausiblyApp for SVG favicon support - Add drag-to-reorder via sh.calvin.reorderable library with a drag handle per site row; order persisted to sort_order column - Add sort_order column to stored_sites with schema migration (1.sqm) - New SiteRepository methods: reorderSites(), deleteSitesForInstance(), getAllSites() now includes sort_order - Dependencies: coil-compose, coil-network-okhttp, coil-svg, reorderable (all Apache 2.0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| gradle | ||
| .gitignore | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| plan.md | ||
| README.md | ||
| settings.gradle.kts | ||
Implausibly
An open-source Android dashboard app for self-hosted Plausible Analytics CE.
The existing third-party apps are all closed-source. If you're running self-hosted Plausible specifically because you care about control and transparency, a closed-source dashboard app is an odd fit.
Implausibly is:
- Fully open source (GPL-3.0) — audit it, fork it, contribute
- Self-hosted first — configurable base URL, no Plausible.io assumptions
- No account or subscription — just your Plausible API key
- Zero telemetry — no analytics in the analytics app
- No Google Play Services — pure AndroidX, F-Droid compatible
Screenshots
Coming soon — the app is functional but screenshots haven't been captured yet.
Features
- Multi-instance support — connect to multiple Plausible servers
- Full dashboard — visitors, pageviews, bounce rate, visit duration
- Visitor chart — custom Canvas line chart with area fill
- Dimension breakdowns — top sources, pages, countries, devices, browsers, OS
- Date range selector — today, 7d, 30d, 6mo, 12mo
- Pull-to-refresh — swipe down to reload
- Caching — SQLDelight cache with TTL per date range; shows cached data instantly on launch
- Encrypted API keys — stored via EncryptedSharedPreferences (AES-256-GCM)
- Clone instances — duplicate an instance config to track a different site with the same credentials
- Smart navigation — remembers your last-viewed dashboard and opens directly to it
- Material 3 — light and dark theme
Requirements
- Android 8.0+ (API 26)
- A self-hosted Plausible CE instance with API v2 enabled
- An API key (or a public shared link)
Building
# Clone
git clone ssh://git@kode.naiv.no:2222/olemd/implausible.git
cd implausible
# Build debug APK
./gradlew assembleDebug
# Run unit tests
./gradlew test
# Install on connected device
./gradlew installDebug
# Lint
./gradlew lint
Requires JDK 17. The Gradle wrapper is included — no separate Gradle installation needed.
Architecture
Four-layer unidirectional data flow:
UI (Jetpack Compose + Material 3)
| observes StateFlow
ViewModel (per screen, sealed UiState)
| calls
Repository (cache-first, coordinates API + DB)
| delegates to
Remote (Ktor) + Local Cache (SQLDelight)
Everything goes through one API endpoint: POST /api/v2/query. The dashboard fires 8 concurrent queries per load.
Tech stack
| Component | Library |
|---|---|
| UI | Jetpack Compose + Material 3 |
| HTTP | Ktor (OkHttp engine) |
| Serialization | kotlinx.serialization |
| Local DB | SQLDelight |
| DI | Hilt |
| Preferences | DataStore |
| Secrets | EncryptedSharedPreferences |
| Charts | Custom Canvas (no library) |
Package structure
no.naiv.implausibly/
data/
remote/ Ktor API client, DTOs
local/ SQLDelight generated code
repository/ StatsRepository, InstanceRepository, SiteRepository, CacheManager
ApiKeyStore Encrypted key storage
AppPreferences DataStore preferences
domain/model/ Domain models (decoupled from API/DB)
di/ Hilt modules
ui/
setup/ Instance onboarding
sites/ Site list / management
dashboard/ Stats dashboard, components, sections
navigation/ NavHost, routes
theme/ Material 3 theme
common/ Shared UI (UiState, LoadingIndicator, ErrorState)
API
The app uses the Plausible Stats API v2. Rate limit is 600 requests/hour — with 8 queries per dashboard load, that's ~75 refreshes/hour.
Caching
Cache key = SHA-256 hash of (instanceId, siteId, dateRange). TTL varies:
| Date range | Cache TTL |
|---|---|
| Realtime | Never cached |
| Today | 5 min |
| 7d, 30d | 30 min |
| 6mo, 12mo | 2 hours |
On app open: show cached data immediately, refresh in background.
Roadmap
Phase 2 (planned)
- Filter system (bottom sheet, combinable filters)
- Material You dynamic color
- Landscape layout
- Realtime polling
- Sites API auto-discovery
- Custom date ranges
- F-Droid submission
Phase 3 (future)
- Comparison mode (this period vs previous)
- Goal conversions
- Segments support
- Home screen widgets (Glance)
License
This means any distributed fork must also be open source under the same license. All dependencies are GPL-compatible (Apache 2.0, MIT).
Contributing
Contributions welcome. The project is hosted on Forgejo at kode.naiv.no.
All source files must include the SPDX header: // SPDX-License-Identifier: GPL-3.0-only