better builtin profiles

This commit is contained in:
stopflock
2025-08-27 21:24:22 -05:00
parent 24b20e8a57
commit 376fa27736
5 changed files with 97 additions and 14 deletions

View File

@@ -14,19 +14,93 @@ class CameraProfile {
this.builtin = false,
});
/// Builtin default: Generic Flock ALPR camera
factory CameraProfile.alpr() => CameraProfile(
id: 'builtin-alpr',
name: 'Generic Flock',
/// Builtin default: Generic ALPR camera (view-only)
factory CameraProfile.genericAlpr() => CameraProfile(
id: 'builtin-generic-alpr',
name: 'Generic ALPR',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
},
builtin: true,
);
/// Builtin: Flock Safety ALPR camera
factory CameraProfile.flock() => CameraProfile(
id: 'builtin-flock',
name: 'Flock',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
'camera:type': 'fixed',
'manufacturer': 'Flock Safety',
'manufacturer:wikidata': 'Q108485435',
},
builtin: true,
);
/// Builtin: Motorola Solutions/Vigilant ALPR camera
factory CameraProfile.motorola() => CameraProfile(
id: 'builtin-motorola',
name: 'Motorola/Vigilant',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
'camera:type': 'fixed',
'manufacturer': 'Motorola Solutions',
'manufacturer:wikidata': 'Q634815',
},
builtin: true,
);
/// Builtin: Genetec ALPR camera
factory CameraProfile.genetec() => CameraProfile(
id: 'builtin-genetec',
name: 'Genetec',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
'camera:type': 'fixed',
'manufacturer': 'Genetec',
'manufacturer:wikidata': 'Q30295174',
},
builtin: true,
);
/// Builtin: Leonardo/ELSAG ALPR camera
factory CameraProfile.leonardo() => CameraProfile(
id: 'builtin-leonardo',
name: 'Leonardo/ELSAG',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
'camera:type': 'fixed',
'manufacturer': 'Leonardo',
'manufacturer:wikidata': 'Q910379',
},
builtin: true,
);
/// Builtin: Neology ALPR camera
factory CameraProfile.neology() => CameraProfile(
id: 'builtin-neology',
name: 'Neology',
tags: const {
'man_made': 'surveillance',
'surveillance:type': 'ALPR',
'camera:type': 'fixed',
'manufacturer': 'Neology, Inc.',
},
builtin: true,
);
/// Returns true if this profile can be used for submissions
bool get isSubmittable {
if (!builtin) return true; // All custom profiles are submittable
// Only the generic ALPR builtin profile is not submittable
return id != 'builtin-generic-alpr';
}
CameraProfile copyWith({
String? id,
String? name,

View File

@@ -30,7 +30,7 @@ class PendingUpload {
direction: j['dir'],
profile: j['profile'] is Map<String, dynamic>
? CameraProfile.fromJson(j['profile'])
: CameraProfile.alpr(),
: CameraProfile.genericAlpr(),
attempts: j['attempts'] ?? 0,
error: j['error'] ?? false,
);

View File

@@ -19,7 +19,12 @@ class ProfileState extends ChangeNotifier {
// Initialize profiles from built-in and custom sources
Future<void> init() async {
// Initialize profiles: built-in + custom
_profiles.add(CameraProfile.alpr());
_profiles.add(CameraProfile.genericAlpr());
_profiles.add(CameraProfile.flock());
_profiles.add(CameraProfile.motorola());
_profiles.add(CameraProfile.genetec());
_profiles.add(CameraProfile.leonardo());
_profiles.add(CameraProfile.neology());
_profiles.addAll(await ProfileService().load());
// Load enabled profile IDs from prefs

View File

@@ -18,7 +18,11 @@ class SessionState extends ChangeNotifier {
AddCameraSession? get session => _session;
void startAddSession(List<CameraProfile> enabledProfiles) {
_session = AddCameraSession(profile: enabledProfiles.first);
final submittableProfiles = enabledProfiles.where((p) => p.isSubmittable).toList();
final defaultProfile = submittableProfiles.isNotEmpty
? submittableProfiles.first
: enabledProfiles.first; // Fallback to any enabled profile
_session = AddCameraSession(profile: defaultProfile);
notifyListeners();
}

View File

@@ -26,8 +26,8 @@ class AddCameraSheet extends StatelessWidget {
Navigator.pop(context);
}
final customProfiles = appState.enabledProfiles.where((p) => !p.builtin).toList();
final allowSubmit = customProfiles.isNotEmpty && !session.profile.builtin;
final submittableProfiles = appState.enabledProfiles.where((p) => p.isSubmittable).toList();
final allowSubmit = submittableProfiles.isNotEmpty && session.profile.isSubmittable;
return Padding(
padding:
@@ -49,7 +49,7 @@ class AddCameraSheet extends StatelessWidget {
title: const Text('Profile'),
trailing: DropdownButton<CameraProfile>(
value: session.profile,
items: appState.enabledProfiles
items: submittableProfiles
.map((p) => DropdownMenuItem(value: p, child: Text(p.name)))
.toList(),
onChanged: (p) =>
@@ -67,7 +67,7 @@ class AddCameraSheet extends StatelessWidget {
onChanged: (v) => appState.updateSession(directionDeg: v),
),
),
if (customProfiles.isEmpty)
if (submittableProfiles.isEmpty)
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
child: Row(
@@ -76,14 +76,14 @@ class AddCameraSheet extends StatelessWidget {
SizedBox(width: 6),
Expanded(
child: Text(
'Enable or create a custom profile in Settings to submit new cameras.',
'Enable a submittable profile in Settings to submit new cameras.',
style: TextStyle(color: Colors.red, fontSize: 13),
),
),
],
),
)
else if (session.profile.builtin)
else if (!session.profile.isSubmittable)
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
child: Row(
@@ -92,7 +92,7 @@ class AddCameraSheet extends StatelessWidget {
SizedBox(width: 6),
Expanded(
child: Text(
'The built-in profile is for map viewing only. Please select a custom profile to submit new cameras.',
'This profile is for map viewing only. Please select a submittable profile to submit new cameras.',
style: TextStyle(color: Colors.orange, fontSize: 13),
),
),