# Traccar Client Fork - Agent Specification This spec describes how to transform a fresh fork of [traccar/traccar-client](https://github.com/traccar/traccar-client) (Flutter) into a simplified, hardcoded tracking client. Apply all changes to the `main` branch. ## Goal Create a minimal Traccar Client that: - Hard-codes all server/tracking settings (no user configuration) - Shows only a device ID and a tracking toggle in the UI - Removes access to settings, status, manual send, QR scanning, and password protection ## Upstream Repository ``` https://github.com/traccar/traccar-client ``` The app is a Flutter project targeting iOS and Android from a single Dart codebase. --- ## 1. Configuration File: `lib/config.dart` Create a new file `lib/config.dart` with compile-time constants. This is the single source of truth for all hardcoded settings. A developer forking this project only needs to edit this file to reconfigure the app. ```dart class AppConfig { AppConfig._(); /// Traccar server URL static const String serverUrl = 'https://c.track.rs'; /// Location accuracy: 'highest', 'high', 'medium', or 'low' static const String accuracy = 'highest'; /// Location update interval in seconds static const int intervalSeconds = 60; /// Distance filter in meters (0 = disabled) static const int distanceFilter = 0; /// Whether to enable stop detection static const bool stopDetection = false; /// Whether to buffer locations when offline static const bool buffer = true; /// Fastest location update interval in seconds (Android) static const int fastestIntervalSeconds = 30; } ``` All other modified files must read from `AppConfig` instead of using inline literals. --- ## 2. Hardcoded Settings (via `AppConfig`) These values are set in `lib/config.dart` and enforced at runtime: | Setting | Constant | Default Value | |--------------------|-----------------------------------|--------------------------| | Server URL | `AppConfig.serverUrl` | `https://c.track.rs` | | Location Accuracy | `AppConfig.accuracy` | `highest` | | Interval | `AppConfig.intervalSeconds` | `60` | | Distance Filter | `AppConfig.distanceFilter` | `0` (disabled) | | Stop Detection | `AppConfig.stopDetection` | `false` (disabled) | | Buffer | `AppConfig.buffer` | `true` | | Fastest Interval | `AppConfig.fastestIntervalSeconds`| `30` | --- ## 3. File Changes ### 3.1 `lib/preferences.dart` Add `import 'config.dart';` at the top. #### `migrate()` method After the existing migration logic and device ID generation, force-set values from `AppConfig`: ```dart // After device ID generation await instance.setString(url, AppConfig.serverUrl); await instance.setString(accuracy, AppConfig.accuracy); await instance.setInt(interval, AppConfig.intervalSeconds); await instance.setInt(distance, AppConfig.distanceFilter); await instance.setBool(buffer, instance.getBool(buffer) ?? AppConfig.buffer); await instance.setBool(stopDetection, AppConfig.stopDetection); await instance.setInt(fastestInterval, instance.getInt(fastestInterval) ?? AppConfig.fastestIntervalSeconds); ``` #### `geolocationConfig()` method Replace dynamic preference reads with `AppConfig` references: - `isHighestAccuracy` = `AppConfig.accuracy == 'highest'` - `locationUpdateInterval` = `AppConfig.intervalSeconds * 1000` (but set to `0` in the config when highest accuracy) - `desiredAccuracy`: Use `DESIRED_ACCURACY_NAVIGATION` on iOS, `DESIRED_ACCURACY_HIGH` on Android (when highest) - `url`: `AppConfig.serverUrl` (passed through `_formatUrl`) - `distanceFilter`: `AppConfig.distanceFilter.toDouble()` - `locationUpdateInterval` (in config): `0` (when highest accuracy, for continuous tracking) - `disableElasticity`: `true` - `disableStopDetection`: `!AppConfig.stopDetection` - `pausesLocationUpdatesAutomatically`: `false` on iOS, `null` on Android - `fastestLocationUpdateInterval`: `0` when highest accuracy, else `AppConfig.fastestIntervalSeconds * 1000` ### 3.2 `lib/main_screen.dart` **Complete rewrite.** Replace the entire file with a minimal screen containing: 1. **AppBar** with title `'Traccar Client'` 2. **Centered body** with two cards: - **Device ID card**: Shows label "Device ID" and a `SelectableText` displaying the device ID in monospace bold (`headlineSmall`) - **Tracking toggle card**: A `SwitchListTile` labeled "Continuous Tracking" with subtitle showing state ("Sending location updates" / "Location tracking disabled") 3. **Tracking toggle behavior**: - On: calls `bg.BackgroundGeolocation.start()` then checks battery optimizations - Off: calls `bg.BackgroundGeolocation.stop()` - Track `isMoving` state; set `activeTrackColor` to error color when `isMoving == false` (stationary warning) 4. **State listeners**: Listen to `onEnabledChange` and `onMotionChange` from BackgroundGeolocation 5. **Battery optimization dialog**: Keep the `_checkBatteryOptimizations` method that shows a dialog using `bg.DeviceSettings` **Remove entirely:** - Settings button/navigation - Status screen button/navigation - "Send Location" button - Password service import and usage - Any reference to `SettingsScreen` or `StatusScreen` navigation **Imports needed:** `flutter/material.dart`, `flutter/services.dart`, `traccar_client/main.dart` (for `messengerKey`), `traccar_client/preferences.dart`, `flutter_background_geolocation`, `l10n/app_localizations.dart` ### 3.3 `lib/settings_screen.dart` **Complete rewrite.** Replace with a minimal read-only screen (not navigable from main UI, but kept for completeness): Add `import 'config.dart';` at the top. - Device ID display (read-only `ListTile`) - Continuous Tracking `SwitchListTile` (functional) - Read-only disabled `ListTile` entries displaying values from `AppConfig`: - Server: `AppConfig.serverUrl` - Location Accuracy: `AppConfig.accuracy` - Distance: `AppConfig.distanceFilter` (show "Disabled" when 0) - Interval: `'${AppConfig.intervalSeconds} seconds'` - Stop Detection: `AppConfig.stopDetection` (show "Disabled"/"Enabled") ### 3.4 `ios/Runner.xcodeproj/project.pbxproj` Find the build phase named `FlutterFire: "flutterfire upload-crashlytics-symbols"` and prepend `exit 0` to the shell script to skip it. This prevents `flutterfire: command not found` build failures. The `shellScript` value should start with: ``` \n#!/bin/bash\n# Skip Firebase Crashlytics for now\nexit 0\n ``` followed by the original script content (which will never execute). --- ## 4. Files NOT Modified These files remain unchanged from upstream: - `lib/main.dart` - App entry point, Firebase init, app links, rate dialog - `lib/geolocation_service.dart` - `lib/configuration_service.dart` - `lib/push_service.dart` - `lib/quick_actions.dart` - `lib/location_cache.dart` - `lib/status_screen.dart` - Kept in codebase but not navigable - `lib/password_service.dart` - Kept in codebase but unused - `lib/qr_code_screen.dart` - Kept in codebase but not navigable - `pubspec.yaml` - All localization files (`lib/l10n/`) - Android platform files --- ## 5. Verification Checklist After applying changes: 1. `flutter pub get` succeeds 2. `flutter build ios` succeeds (no flutterfire errors) 3. `flutter build apk --release` succeeds 4. App launches showing only device ID and tracking toggle 5. No settings, status, or send location buttons visible 6. Toggling tracking on starts background location reporting to `https://c.track.rs` 7. Device ID is auto-generated (8-digit random number) and persists across launches 8. Location updates use highest accuracy with 0 distance filter 9. Stop detection is disabled (tracking never auto-pauses)