T22/T24/T26: uploader, integration tests, and release readiness
T22 -- TelemetryUploader on package:http with MockClient standing in for MockWebServer, tested against a real in-memory Drift database rather than a fake DAO. Upload runs on its own timer, injected as a callback so the engine has no opinion about HTTP and tests need no network. Config on shared_preferences with a hand-rolled UUID v4. Still no UI for the endpoint, exactly as in the native app. 7 tests. T24 -- integration_test/app_test.dart, 4 tests passing on the iOS simulator. These cover what widget tests cannot: Drift opening against real platform storage, plugin registration, go_router driving a real Navigator, cold start. T26 -- RELEASE-IOS.md. Usage strings are specific rather than generic, which is the leading Guideline 5.1.1 rejection cause; privacy-label answers decided; a pre-submission list covering the bundle-id switch back to com.rippr, the still default app icon, a release build, and a demo video for review notes. T25 written up as REAL-RIDE-CHECKLIST.md but outstanding by nature. Two items decide real things: force-stopping mid-ride on Android confirms the 111 km crash-gap bug is fixed, and parking 15 minutes mid-recording on iOS decides whether geolocator is sufficient or the paid engine is needed. Two prefer_initializing_formals lints suppressed with a reason: Dart forbids a named parameter beginning with an underscore, so the suggested fix will not compile. 178 tests passing, analyze clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
61
lib/src/config/config.dart
Normal file
61
lib/src/config/config.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
/// Ported from `com.rippr.Config`.
|
||||
///
|
||||
/// Runtime configuration. The upload endpoint is intentionally empty by default —
|
||||
/// recording must work with no server at all, and uploading is opt-in.
|
||||
library;
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const _keyEndpoint = 'upload_endpoint';
|
||||
const _keyDeviceId = 'device_id';
|
||||
const _keyMapEnabled = 'map_enabled';
|
||||
|
||||
class Config {
|
||||
Config(this._prefs);
|
||||
|
||||
final SharedPreferences _prefs;
|
||||
|
||||
static Future<Config> load() async =>
|
||||
Config(await SharedPreferences.getInstance());
|
||||
|
||||
String get uploadEndpoint => _prefs.getString(_keyEndpoint) ?? '';
|
||||
|
||||
Future<void> setUploadEndpoint(String url) =>
|
||||
_prefs.setString(_keyEndpoint, url.trim());
|
||||
|
||||
/// Whether trip detail renders a map at all.
|
||||
///
|
||||
/// When off the map widget is never created, so no tile is ever requested — a real
|
||||
/// short-circuit, not a hidden view. The map only ever exists inside a visible screen's
|
||||
/// lifecycle; the recorder never touches it.
|
||||
bool get mapEnabled => _prefs.getBool(_keyMapEnabled) ?? true;
|
||||
|
||||
Future<void> setMapEnabled(bool enabled) =>
|
||||
_prefs.setBool(_keyMapEnabled, enabled);
|
||||
|
||||
/// Stable per-install id so a server can distinguish riders in a group.
|
||||
String get deviceId {
|
||||
final existing = _prefs.getString(_keyDeviceId);
|
||||
if (existing != null) return existing;
|
||||
final generated = _randomId();
|
||||
// Fire-and-forget: the value is returned immediately either way, and a lost write
|
||||
// only costs a new id next launch.
|
||||
_prefs.setString(_keyDeviceId, generated);
|
||||
return generated;
|
||||
}
|
||||
|
||||
/// A UUID v4, without pulling in a package for sixteen bytes.
|
||||
static String _randomId() {
|
||||
final rng = Random.secure();
|
||||
final bytes = List<int>.generate(16, (_) => rng.nextInt(256));
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
String hex(int start, int end) => bytes
|
||||
.sublist(start, end)
|
||||
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
||||
.join();
|
||||
return '${hex(0, 4)}-${hex(4, 6)}-${hex(6, 8)}-${hex(8, 10)}-${hex(10, 16)}';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user