trcr/SPEC.md

189 lines
7.7 KiB
Markdown
Raw Normal View History

2026-03-06 12:26:57 +11:00
# 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)