155 lines
4.6 KiB
Markdown
155 lines
4.6 KiB
Markdown
# Implausibly
|
|
|
|
An open-source Android dashboard app for self-hosted [Plausible Analytics](https://plausible.io/) 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
|
|
|
|
```bash
|
|
# 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](https://plausible.io/docs/stats-api). 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
|
|
|
|
[GPL-3.0-only](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`
|