Visla GPS β Android App
What is Visla GPS?β
Visla GPS is a real-time GPS tracking and device management application for Android. It connects to GPS tracking devices to provide live location monitoring, trip history, geofencing, remote commands, and device sharing β all backed by a subscription-based billing model through Google Play.
Who is it for?β
- Fleet managers β monitor vehicle fleets in real time with geofence alerts and trip history
- Vehicle owners β track personal vehicles with theft alerts and location sharing
- Delivery services β coordinate drivers with live map views and command dispatch
- Families β share device locations with family members for peace of mind
Key Featuresβ
| Feature | Description |
|---|---|
| Live Map Tracking | Real-time device positions on Google Maps or Mapbox with WebSocket updates |
| Device Management | Add devices via QR code or manual entry, configure settings, view status |
| Trip History | Browse past trips with route playback and event timeline |
| Geofencing | Create, edit, and manage geofence zones with enter/exit notifications |
| Device Sharing | Share device access with other users via invite system |
| Remote Commands | Send commands to devices (engine cut, door lock, etc.) with history tracking |
| Push Notifications | Firebase FCM-powered alerts for geofence events, device status, and more |
| Home Screen Widget | Glance-based widget showing device grid with status at a glance |
| Subscription Billing | Google Play Billing integration with plan management |
| Multi-Provider Maps | Switch between Google Maps and Mapbox per user preference |
| Social Login | Google Sign-In and Facebook Login alongside email/password auth |
| Two-Factor Auth | TOTP-based 2FA with QR code setup via ML Kit and ZXing |
Tech Stackβ
| Category | Technology |
|---|---|
| Language | Kotlin 2.3.10 |
| UI Framework | Jetpack Compose (BOM 2026.02.00) + Material 3 |
| DI | Hilt 2.59.2 (with KSP) |
| Networking | Retrofit 3.0.0 / OkHttp 5.3.2 / WebSocket |
| Maps | Google Maps Compose 8.1.0 + Mapbox 11.18.1 |
| Push Notifications | Firebase Cloud Messaging (BOM 34.9.0) |
| Local Storage | DataStore Preferences (encrypted via Tink 1.20.0) |
| Secure Storage | EncryptedSharedPreferences (security-crypto 1.1.0) |
| Billing | Google Play Billing 8.3.0 |
| Navigation | Navigation Compose 2.9.7 |
| Widgets | Glance AppWidget 1.1.1 + WorkManager 2.11.1 |
| Camera / QR | CameraX 1.5.3 + ML Kit Barcode 17.3.0 + ZXing 3.5.4 |
| Auth Providers | Google Identity (Credential Manager 1.5.0) + Facebook Login 18.1.3 |
| Linting | ktlint 1.5.0, detekt 1.23.8, Android Lint |
| Testing | JUnit 4, Mockito 5.21.0, MockK 1.14.9, Turbine 1.2.1, Compose UI Tests |
Build Configurationβ
| Property | Value |
|---|---|
| Android Gradle Plugin | 9.0.1 |
| Kotlin | 2.3.10 (via kotlin-gradle-plugin, overriding AGP built-in) |
| KSP | 2.3.6 |
| Java Toolchain | 21 |
minSdk | 26 (Android 8.0 Oreo) |
targetSdk / compileSdk | 36 |
| Compose BOM | 2026.02.00 |
| Configuration Cache | Enabled |
| Core Library Desugaring | desugar_jdk_libs 2.1.5 |
AGP 9.0 uses built-in Kotlin support (android.builtInKotlin=true in gradle.properties), with an explicit higher Kotlin version applied via the buildscript classpath.
Project Structureβ
com.visla.vislagps/
βββ MainActivity.kt # Single-activity entry point
βββ VislaGPSApp.kt # Application class (Hilt entry point)
β
βββ core/ # Core infrastructure
β βββ cache/ # In-memory caching
β βββ config/ # Deep link configuration
β βββ data/ # Core data utilities
β βββ geometry/ # Geo math helpers
β βββ initialization/ # App startup logic
β βββ network/ # Auth interceptor, token management, encrypted storage
β βββ notification/ # Notification channel & display manager
β βββ permissions/ # Runtime permission handling
β βββ service/ # FCM service & token manager
β βββ utils/ # Shared utilities
β
βββ data/ # Data layer
β βββ auth/ # Google & Facebook auth managers
β βββ billing/ # Google Play Billing manager
β βββ mappers/ # DTO β entity mappers
β βββ models/ # Data models
β βββ network/ # WebSocket manager
β βββ remote/
β β βββ api/ # Retrofit API interfaces (Auth, Device, Geofence, etc.)
β β βββ dto/ # Data transfer objects
β βββ repositories/ # Repository implementations
β
βββ domain/ # Domain layer
β βββ entities/ # Business entities (Device, Position, Geofence, User, etc.)
β βββ errors/ # Domain error types
β βββ repositories/ # Repository interfaces
β βββ services/ # Service interfaces
β βββ usecases/ # Use cases organized by feature
β β βββ auth/ # Authentication flows
β β βββ billing/ # Subscription management
β β βββ commands/ # Remote device commands
β β βββ deeplink/ # Deep link resolution
β β βββ devices/ # Device CRUD
β β βββ events/ # Event retrieval
β β βββ geofences/ # Geofence management
β β βββ notifications/ # Notification preferences
β β βββ positions/ # Position history
β β βββ realtime/ # WebSocket real-time updates
β β βββ sharing/ # Device sharing & invites
β βββ ValidationConstants.kt # Shared validation rules
β
βββ di/ # Hilt dependency injection modules
β βββ AppModule.kt
β βββ AppPreferencesModule.kt
β βββ BaseUrlModule.kt
β βββ BillingModule.kt
β βββ NetworkModule.kt
β βββ RepositoryModule.kt
β βββ ServiceModule.kt
β βββ TokenManagerModule.kt
β
βββ ui/ # Presentation layer
β βββ base/ # Base composable patterns
β βββ components/ # Reusable UI components
β β βββ common/ # Buttons, dialogs, inputs
β β βββ device/ # Device cards & lists
β β βββ geofence/ # Geofence editors
β β βββ map/ # Map overlays
β β βββ notifications/ # Notification items
β β βββ settings/ # Settings rows
β β βββ subscription/ # Billing UI
β βββ formatters/ # Display formatting utilities
β βββ handlers/ # UI event handlers
β βββ maps/ # Google Maps & Mapbox composables
β βββ navigation/ # Navigation graph & bottom bar
β βββ screens/ # Feature screens (one file per screen + ViewModel)
β βββ state/ # UI state holders
β βββ subscription/ # Subscription flow
β βββ theme/ # Material 3 theme (colors, typography, dimensions)
β βββ utils/ # UI utilities
β βββ viewmodels/ # Shared ViewModels
β
βββ widget/ # Home screen widget
βββ DeviceGridWidget.kt # Glance widget definition
βββ WidgetDataStore.kt # Widget data persistence
βββ WidgetDevice.kt # Widget device model
βββ WidgetGridBuilder.kt # Grid layout builder
βββ WidgetSyncWorker.kt # Background sync via WorkManager
Getting Startedβ
Prerequisitesβ
- Android Studio Meerkat (2024.3+) or later with AGP 9.0 support
- JDK 21 (bundled with Android Studio at
/opt/android-studio/jbr) - Android SDK with API 36 platform installed
- Mapbox account for Mapbox SDK downloads token
- Physical device or emulator (API 26+)
Setupβ
-
Clone the repository and navigate to the Android project:
cd android -
Create
local.propertiesfrom the example:cp local.properties.example local.properties -
Fill in required values in
local.properties:sdk.dir=/path/to/your/Android/Sdk
GOOGLE_MAPS_API_KEY=your_google_maps_api_key
MAPBOX_ACCESS_TOKEN=your_mapbox_access_token
FACEBOOK_APP_ID=your_facebook_app_id
FACEBOOK_CLIENT_TOKEN=your_facebook_client_token -
Set the Mapbox downloads token (required to resolve Mapbox Maven dependencies):
export MAPBOX_DOWNLOADS_TOKEN=your_mapbox_downloads_tokenOr add to
~/.gradle/gradle.properties:MAPBOX_DOWNLOADS_TOKEN=your_mapbox_downloads_token -
Build the project:
make build
Local Developmentβ
For local backend development, uncomment and set the environment URLs in local.properties:
API_BASE_URL=http://10.0.2.2:8080
WS_BASE_URL=ws://10.0.2.2:8080
Note:
10.0.2.2is the Android emulator alias for the host machine'slocalhost.
Use make dev to build, install, set up ADB port forwarding, and launch the app in one step.
Makefile Targetsβ
| Target | Description |
|---|---|
make help | Show all available targets with usage examples |
make build | Build debug APK (assembleDebug) |
make release | Build release AAB (bundleRelease) |
make install | Build and install debug APK to connected device |
make launch | Launch the app on a connected device |
make dev | Full dev workflow: build β install β port-forward β launch |
make uninstall | Uninstall the app from the device |
make test-unit | Run all unit tests |
make test-unit test=X | Run unit tests matching a pattern (e.g., test=DevicesViewModel) |
make test-ui | Run instrumented UI tests on a connected device/emulator |
make lint | Run all linters (ktlint + detekt + Android Lint) |
make ktlint | Run ktlint code style checks |
make ktlint-fix | Run ktlint with auto-fix |
make detekt | Run detekt static analysis |
make lint-android | Run Android Lint |
make clean | Clean build artifacts |
make clean-adb | Reset ADB server |
make clean-all | Clean build + reset ADB |
make logs | Tail app logs filtered by PID, grepping for visla, Exception, Error |
make devices | List connected ADB devices |
make connect | Connect to a device over WiFi |
make emu | Start an emulator (EMU=name) |
make emulators | List available AVD emulators |
make stop-emu | Stop the running emulator |
Device selection is controlled by the VIA variable:
make dev # WiFi (default, DEVICE=android:5555)
make dev VIA=usb # USB-connected device
make dev VIA=emu # Running emulator
make install VIA=usb # Install via USB
make logs VIA=emu # Logs from emulator
Architectureβ
The app follows Clean Architecture with three layers:
- UI β Domain: ViewModels invoke use cases; never touch repositories directly.
- Domain β Data: Domain defines repository interfaces; data provides implementations.
- Dependency rule: Inner layers have no knowledge of outer layers. The domain layer has zero Android framework dependencies.
Design Decisionsβ
Jetpack Compose over XML Viewsβ
The entire UI is built with Compose. It eliminates the fragment lifecycle complexity, reduces boilerplate, and enables a declarative, state-driven UI model that pairs naturally with Kotlin coroutines and flows. The theme system uses Material 3 with custom color, typography, and dimension tokens defined in ui/theme/.
Hilt over Manual DIβ
Hilt provides compile-time verified dependency injection with minimal boilerplate. The di/ package contains eight focused modules (NetworkModule, RepositoryModule, BillingModule, etc.) that keep bindings organized by concern. KSP is used instead of kapt for faster annotation processing.
Clean Architecture with Use Casesβ
Each user-facing operation is encapsulated in a dedicated use case class under domain/usecases/, organized by feature. This keeps ViewModels thin β they orchestrate use cases rather than containing business logic β and makes the domain layer independently testable without Android dependencies.
Dual Map Provider (Google Maps + Mapbox)β
The app supports both Google Maps and Mapbox through a MapProviderManager abstraction in the data layer. Map composables in ui/maps/ provide provider-specific implementations (GoogleMapContent, MapboxMapContent) behind a unified MapContent interface. This gives users a choice and reduces vendor lock-in.
Encrypted Token Storageβ
Authentication tokens are stored using DataStore Preferences encrypted with Google Tink (EncryptedTokenDataStore). This provides at-rest encryption without the limitations of EncryptedSharedPreferences (which is still available for legacy secure storage). Token refresh is handled transparently by TokenAuthenticator and AuthInterceptor in the OkHttp pipeline.
WebSocket for Real-Time Updatesβ
Live device positions are delivered over a persistent WebSocket connection managed by WebSocketManager. This avoids polling overhead and provides sub-second position updates on the map. The connection lifecycle is tied to the app's foreground state via lifecycle-process.
Glance Widgets with WorkManager Syncβ
The home screen widget uses Jetpack Glance (Compose-based widget framework) rather than legacy RemoteViews. WidgetSyncWorker periodically refreshes device data in the background via WorkManager, keeping the widget up to date without requiring the app to be open.
AGP 9.0 with Built-In Kotlinβ
The project uses Android Gradle Plugin 9.0's built-in Kotlin support (android.builtInKotlin=true) with an explicit higher Kotlin version (2.3.10) applied via the buildscript classpath. This simplifies the plugin setup while allowing use of the latest Kotlin features.
Fake Billing in Debugβ
Debug builds set USE_FAKE_BILLING=true via BuildConfig, allowing the billing flow to be tested without a Google Play connection. Release builds always use the real billing client.