trcr/SPEC.md

7.7 KiB

Traccar Client Fork - Agent Specification

This spec describes how to transform a fresh fork of 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.

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:

// 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)