Modern Android time tracking client for Solidtime
A native Kotlin/Jetpack Compose Android application that connects to Solidtime for secure, OAuth2-based time tracking on mobile devices.
- Start/stop time entries with a live elapsed timer
- Pause and resume active timers
- Select projects and tasks from searchable dropdowns
- Add descriptions, tags, and billable status
- Continue the most recent completed entry
- Edit or delete past time entries
- View history grouped by date, with identical entries collapsed into expandable groups
- Pull-to-refresh to sync with the server
- Switch between all organizations available to your Solidtime account
- Quick Settings tile - Start/stop tracking without opening the app, with project and task selection
- Persistent notification - Shows elapsed time with pause and stop controls
- Notification quick start - Start a timer with project, task, and description selection directly from the idle notification
- Home-screen widget - View tracking state and access quick controls
- Boot persistence - Restores notification state after reboot
- OAuth2 with PKCE (no API keys needed)
- Automatic token refresh
- Custom server endpoints and OAuth client IDs
- Server details available in the settings drawer with tap-to-copy
- Encrypted token storage
- Material 3 with Material You dynamic colors (Android 12+)
- Dark and light themes follow system settings
- Edge-to-edge display with predictive back gesture support (Android 13+)
- Available in English, Dutch, and Japanese + any language you want if you create a pull request
- The Quick Settings tile state won't sync with changes made on the web or desktop unless the app is open. Not a problem if you only use the tile and app.
The shots below are generated on the JVM (no device) by the Roborazzi + Robolectric suite — see
Testing. Run ./gradlew :app:recordRoborazziDebug to regenerate them.
Rendered in the Neo dark theme.
The Live activity shots below were captured on a connected Android 16 device.

Live activity
Promoted ongoing notification while tracking

Compact live activity
Collapsed status bar presentation
- Language: Kotlin
- UI Framework: Jetpack Compose with Material 3
- Architecture: MVVM with Clean Architecture
- Dependency Injection: Hilt
- Networking: Retrofit + OkHttp
- Serialization: Kotlinx Serialization
- Async: Kotlin Coroutines + Flow
- Storage: DataStore Preferences with Android Keystore-backed AES-GCM encryption for OAuth secrets
- Jetpack Compose - Declarative UI framework
- Hilt - Dependency injection
- Retrofit - Type-safe HTTP client
- OkHttp - HTTP interceptors and authentication
- Kotlinx Serialization - JSON serialization
- DataStore + Android Keystore - Tokens and transient PKCE values are encrypted with AES-GCM; non-secret app settings remain ordinary preferences
- Custom Tabs - Secure OAuth browser flow
- Timber - Logging
By default, SolidVerdant connects to the official Solidtime instance at https://app.solidtime.io. To configure a custom server:
- Launch the app
- On the login screen, tap the settings icon (⚙️) in the top bar
- Enter your custom server endpoint and OAuth client ID
- Tap "Save"
Default Configuration:
- Server Endpoint:
https://app.solidtime.io - Client ID:
9c994748-c593-4a6d-951b-6849c829bc4e
run docker exec solidtime-scheduler php artisan passport:client --name=desktop --redirect_uri=solidtime://oauth/callback --public -n
Goal of this application is to have a easy to use tile to start and stop tracking, this application does everything i need it to but pullrequests for other features are welcome, forking is fine too
- Login: Tap "Login with OAuth2" to authenticate via your Solidtime account
- View Tracking: See your current time entry with live elapsed time
- Switch Organization: Tap the organization name in the header while no timer is active
- Refresh: Pull to refresh or tap the refresh button to update tracking state
- Logout: Open the navigation drawer and tap logout to clear all data and return to login screen
./gradlew assembleDebug- Debug (
app-debug.apk) - Includes logging, no minification, package suffix.dev - Release (
app-release.apk) - ProGuard enabled, resources shrunk, signed for distribution
Run unit tests:
./gradlew testRun instrumentation tests:
./gradlew connectedAndroidTestGenerate the README screenshots (pure JVM, no device/emulator — Roborazzi + Robolectric):
./gradlew :app:recordRoborazziDebugThis renders every screen across a light/dark × phone/tablet matrix into .github/screenshots/generated/
and writes the Neo-dark phone hero set to .github/screenshots/readme/.
Use the pinned development environment for Android verification:
devenv tasks run android:gate
devenv tasks run android:gate:instrumentation
devenv tasks run android:e2e:mockThe host gate is device-free. The instrumentation gate mirrors the CI connectedCheck and requires
an already-connected emulator or Android device. The mock E2E task builds and installs the debug APKs
and runs the device suite against a deterministic local backend. Live-portable tests can be run against
an isolated local Solidtime server:
devenv up -d
devenv tasks run solidtime:testThe live task starts the official Solidtime container with disposable PostgreSQL data, resets its
test account, and runs only E2E tests marked @BackendPortable. It requires one authorized physical
Android device; emulator serials are rejected. Set ANDROID_SERIAL if more than one physical device
is connected. The task uses adb reverse and removes the temporary test session from the device
afterward. Do not use a personal Solidtime account or print the generated session file.
The reset removes all time entries from the disposable account and seeds Live Test Project, Live Test Task, and Live Test Tag for metadata-edit tests. It is also available explicitly with
devenv tasks run solidtime:reset. devenv tasks run android:e2e is the mock-suite alias, and
devenv tasks run android:e2e:real is the live-suite alias. The default container image is the
stable solidtime/solidtime:latest; set SOLIDTIME_IMAGE_TAG=main only for an intentional upstream
development compatibility run. The local API uses Solidtime's testing environment so the production
per-user request throttle does not turn a rapid isolated suite into a false failure.
If a restricted development environment reports read-only Podman state under /run/user or cannot
open the Gradle distribution lock, rerun the same pinned devenv task with the approved elevated
container/build permission. These errors are environment restrictions, not Solidtime test results.
Read the JUnit instrumentation summary to determine pass/fail; do not rely on a task wrapper's shell
exit code when it suppresses the instrumentation transcript.
You can verify the authenticity of SolidVerdant APKs using the signing certificate hash below. This works with AppVerifier or Obtainium's built-in verification.
| Field | Value |
|---|---|
| Package ID | dev.tricked.solidverdant |
| SHA-256 Signing Certificate | 7A:38:2F:E9:14:1B:D3:DC:A4:C4:82:20:7F:FF:12:5A:82:8D:66:92:C4:0E:5A:BC:30:61:6C:33:15:C7:F3:64 |
You can also view this information in-app via Settings > About > Verification Info.
- Solidtime Website: https://www.solidtime.io/
- Solidtime Web App: https://app.solidtime.io
- Built with Jetpack Compose
- Architecture patterns from Android Architecture Samples
Made with ❤️ (and almost entirely Codex) for the Solidtime community








