68 lines
2.0 KiB
Dart
68 lines
2.0 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
/// Persistent protocol log for field debugging without a USB cable.
|
|
///
|
|
/// Every line is kept in a bounded in-memory ring (viewable in-app) and, once
|
|
/// [init] has run, appended to a file that can be pulled with adb:
|
|
///
|
|
/// adb pull /sdcard/Android/data/dev.teamhydra.openscooter/files/openscooter.log
|
|
///
|
|
/// Before [init], or in tests, only the in-memory ring is used.
|
|
/// The PIN is never written here; callers mask it first.
|
|
class ProtocolLog extends ChangeNotifier {
|
|
ProtocolLog._();
|
|
static final ProtocolLog instance = ProtocolLog._();
|
|
|
|
static const maxLines = 2000;
|
|
|
|
final List<String> _lines = <String>[];
|
|
File? _file;
|
|
IOSink? _sink;
|
|
|
|
List<String> get lines => List.unmodifiable(_lines);
|
|
String? get path => _file?.path;
|
|
|
|
Future<void> init() async {
|
|
if (_file != null) return;
|
|
try {
|
|
Directory? dir;
|
|
if (Platform.isAndroid) dir = await getExternalStorageDirectory();
|
|
dir ??= await getApplicationDocumentsDirectory();
|
|
_file = File('${dir.path}/openscooter.log');
|
|
_sink = _file!.openWrite(mode: FileMode.append);
|
|
log('LOG', 'opened ${_file!.path}');
|
|
} catch (e) {
|
|
debugPrint('ProtocolLog: could not open log file: $e');
|
|
}
|
|
}
|
|
|
|
void log(String tag, String message) {
|
|
final line = '${DateTime.now().toIso8601String()} [$tag] $message';
|
|
if (kDebugMode) debugPrint(line);
|
|
_lines.add(line);
|
|
if (_lines.length > maxLines) _lines.removeRange(0, _lines.length - maxLines);
|
|
_sink?.writeln(line);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> clear() async {
|
|
_lines.clear();
|
|
await _sink?.flush();
|
|
await _sink?.close();
|
|
_sink = null;
|
|
final f = _file;
|
|
if (f != null) {
|
|
try {
|
|
await f.writeAsString('');
|
|
} catch (_) {}
|
|
_sink = f.openWrite(mode: FileMode.append);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> flush() async => _sink?.flush();
|
|
}
|