This commit is contained in:
stopflock
2025-08-30 23:17:08 -05:00
parent a17c50188e
commit fa16e3c299
21 changed files with 124 additions and 66 deletions

View File

@@ -1,21 +1,21 @@
# Flock Map App
# DeFlock
A comprehensive Flutter app for viewing and mapping surveillance cameras with OpenStreetMap. Includes offline capabilities, editing ability, and an intuitive interface.
A comprehensive Flutter app for mapping public surveillance infrastructure with OpenStreetMap. Includes offline capabilities, editing ability, and an intuitive interface.
**Stop Flock** is a privacy-focused initiative to document the rapid expansion of ALPRs and AI surveillance cameras. This app aims to be the go-to tool for contributors to map cameras in their communities and upload the data to OpenStreetMap, making surveillance infrastructure visible and searchable.
**DeFlock** is a privacy-focused initiative to document the rapid expansion of ALPRs, AI surveillance cameras, and other public surveillance infrastructure. This app aims to be the go-to tool for contributors to map surveillance devices in their communities and upload the data to OpenStreetMap, making surveillance infrastructure visible and searchable.
**For complete documentation, tutorials, and community info, visit [stopflock.com/app](https://stopflock.com/app)**
**For complete documentation, tutorials, and community info, visit [deflock.me](https://deflock.me)**
---
## What This App Does
- **Map surveillance cameras** with precise location, direction, and manufacturer details
- **Map surveillance infrastructure** including cameras, ALPRs, gunshot detectors, and more with precise location, direction, and manufacturer details
- **Upload to OpenStreetMap** with OAuth2 integration (live or sandbox modes)
- **Work completely offline** with downloadable map areas and camera data, plus upload queue
- **Work completely offline** with downloadable map areas and device data, plus upload queue
- **Multiple map types** including satellite imagery from Google, Esri, Mapbox, and OpenStreetMap, plus custom map tile provider support
- **Editing Ability** to update existing camera locations and properties
- **Built-in camera profiles** for Flock Safety, Motorola, Genetec, Leonardo, and other major manufacturers, plus custom profiles for more specific tag sets
- **Editing Ability** to update existing device locations and properties
- **Built-in device profiles** for Flock Safety, Motorola, Genetec, Leonardo, and other major manufacturers, plus custom profiles for more specific tag sets
---
@@ -25,13 +25,13 @@ A comprehensive Flutter app for viewing and mapping surveillance cameras with Op
- **Multi-source tiles**: Switch between OpenStreetMap, Google Satellite, Esri imagery, Mapbox, and any custom providers
- **Offline-first design**: Download a region for complete offline operation
- **Smooth UX**: Intuitive controls, follow-me mode with GPS rotation, and gesture-friendly interactions
- **Camera visualization**: Color-coded markers showing real cameras (blue), pending uploads (purple), new cameras (white), edited cameras (grey), and cameras being edited (orange)
- **Device visualization**: Color-coded markers showing real devices (blue), pending uploads (purple), new devices (white), edited devices (grey), and devices being edited (orange)
### Camera Management
- **Comprehensive profiles**: Built-in profiles for major manufacturers (Flock, Motorola/Vigilant, Genetec, Leonardo/ELSAG, Neology) plus custom profile creation
- **Editing capabilities**: Update location, direction, and tags of existing cameras
### Device Management
- **Comprehensive profiles**: Built-in profiles for major manufacturers (Flock Safety, Motorola/Vigilant, Genetec, Leonardo/ELSAG, Neology) plus custom profile creation
- **Editing capabilities**: Update location, direction, and tags of existing devices
- **Direction visualization**: Interactive field-of-view cones showing camera viewing angles
- **Bulk operations**: Tag multiple cameras efficiently with profile-based workflow
- **Bulk operations**: Tag multiple devices efficiently with profile-based workflow
### Professional Upload & Sync
- **OpenStreetMap integration**: Direct upload with full OAuth2 authentication
@@ -41,7 +41,7 @@ A comprehensive Flutter app for viewing and mapping surveillance cameras with Op
### Offline Operations
- **Smart area downloads**: Automatically calculate tile counts and storage requirements
- **Camera caching**: Offline areas include camera data for complete functionality without network
- **Device caching**: Offline areas include surveillance device data for complete functionality without network
- **Global base map**: Permanent worldwide coverage at low zoom levels
- **Robust downloads**: Exponential backoff, retry logic, and progress tracking for reliable area downloads
@@ -50,18 +50,18 @@ A comprehensive Flutter app for viewing and mapping surveillance cameras with Op
## Quick Start
1. **Install** the app on iOS or Android
2. **Enable location** and grant camera permissions
2. **Enable location** permissions
3. **Log into OpenStreetMap**: Choose upload mode and get OAuth2 credentials
4. **Add your first camera**: Tap the "tag camera" button, position the pin, set direction, select a profile, and tap submit
4. **Add your first device**: Tap the "tag node" button, position the pin, set direction, select a profile, and tap submit
**New to OpenStreetMap?** Visit [stopflock.com/app](https://stopflock.com/app) for complete setup instructions and community guidelines.
**New to OpenStreetMap?** Visit [deflock.me](https://deflock.me) for complete setup instructions and community guidelines.
---
## For Developers
### Architecture Highlights
- **Unified data provider**: All map tiles and camera data route through `MapDataProvider` with pluggable remote/local sources
- **Unified data provider**: All map tiles and surveillance device data route through `MapDataProvider` with pluggable remote/local sources
- **Modular settings**: Each settings section is a separate widget for maintainability
- **State management**: Provider pattern with clean separation of concerns
- **Offline-first**: Network calls are optional; app functions fully offline with downloaded data and queues uploads until online
@@ -93,26 +93,26 @@ flutter run
- Optional custom icons for camera profiles
- Camera deletions
- Clean up cache when submitted changesets appear in Overpass results
- Upgrade camera marker design (considering nullplate's svg)
- Upgrade device marker design (considering nullplate's svg)
### Future Features & Wishlist
- Location-based notifications when approaching cameras
- Red/yellow ring for cameras missing specific tag details
- Location-based notifications when approaching surveillance devices
- Red/yellow ring for devices missing specific tag details
- iOS/Android native themes and dark mode support
- "Cache accumulating" offline areas?
- "Offline areas" as tile provider?
- Jump to location by coordinates, address, or POI name
- Route planning that avoids surveillance cameras
- Custom camera providers and OSM/Overpass alternatives
- Route planning that avoids surveillance devices
- Custom device providers and OSM/Overpass alternatives
---
## Contributing & Community
This app is part of the larger **Stop Flock** initiative. Join the community:
This app is part of the larger **DeFlock** initiative. Join the community:
- **Documentation & Guides**: [stopflock.com/app](https://stopflock.com/app)
- **Community Discussion**: [stopflock.com](https://stopflock.com)
- **Documentation & Guides**: [deflock.me](https://deflock.me)
- **Community Discussion**: [deflock.me](https://deflock.me)
- **Issues & Feature Requests**: GitHub Issues
- **Development**: See developer setup above
@@ -120,7 +120,7 @@ This app is part of the larger **Stop Flock** initiative. Join the community:
## Privacy & Ethics
This project helps make existing public surveillance infrastructure transparent and searchable. We only document cameras that are already installed and visible in public spaces.
This project helps make existing public surveillance infrastructure transparent and searchable. We only document surveillance devices that are already installed and visible in public spaces.
No user information is ever collected, and no data leaves your device except submissions to OSM and whatever data your tile provider can glean from your requests.

26
REBRAND_PROGRESS.md Normal file
View File

@@ -0,0 +1,26 @@
# DeFlock Rebrand Progress
## TODO
- [ ] Test that the app still compiles and runs correctly
## IN PROGRESS
- [ ] Nothing currently
## FINISHED
- [x] pubspec.yaml (package name, description)
- [x] lib/main.dart (app title, class names, theme colors)
- [x] lib/screens/home_screen.dart (app bar title)
- [x] lib/services/auth_service.dart (redirect scheme)
- [x] lib/dev_config.dart (client name)
- [x] android/app/src/main/AndroidManifest.xml (app label, redirect scheme)
- [x] ios/Runner/Info.plist (display name, bundle name, redirect scheme)
- [x] android/app/build.gradle.kts (application ID)
- [x] android/app/src/main/kotlin/... (MainActivity package and directory structure)
- [x] assets/info.txt (about content)
- [x] README.md (all branding references)
- [x] Update all import statements (package:flock_map_app -> package:deflockapp)
- [x] lib/widgets/map/tile_layer_manager.dart (user agent package name)
- [x] test/models/pending_upload_test.dart (imports and CameraProfile -> NodeProfile)
- [x] test/widget_test.dart (import statement)
- [x] linux/CMakeLists.txt (binary name and application ID)
- [x] windows/CMakeLists.txt (project name and binary name)

View File

@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.flock_map_app"
namespace = "me.deflock.deflockapp"
// Matches current stable Flutter (compileSdk 34 as of July 2025)
compileSdk = 35
@@ -24,7 +24,7 @@ android {
defaultConfig {
// Application ID (package name)
applicationId = "com.example.flock_map_app"
applicationId = "me.deflock.deflockapp"
// ────────────────────────────────────────────────────────────
// oauth2_client 4.x & flutter_web_auth_2 5.x require minSdk 23

View File

@@ -9,7 +9,7 @@
<application
android:name="${applicationName}"
android:label="flock_map_app"
android:label="DeFlock"
android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">
<!-- Main Flutter activity -->
@@ -44,8 +44,8 @@
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- flockmap://auth -->
<data android:scheme="flockmap" android:host="auth"/>
<!-- deflockapp://auth -->
<data android:scheme="deflockapp" android:host="auth"/>
</intent-filter>
</activity>

View File

@@ -1,5 +0,0 @@
package com.example.flock_map_app
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@@ -0,0 +1,5 @@
package me.deflock.deflockapp
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

27
assets/deflock-logo.svg Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 1150 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect id="Artboard1" x="0" y="0" width="1150" height="300" style="fill:none;"/>
<g id="Artboard11" serif:id="Artboard1">
<g>
<g transform="matrix(344.475,0,0,344.475,30.1181,267.042)">
<path d="M0.377,-0.658L0.377,-0.655C0.421,-0.629 0.415,-0.593 0.415,-0.547L0.415,-0.415C0.373,-0.452 0.317,-0.473 0.261,-0.473C0.124,-0.473 0.024,-0.364 0.024,-0.229C0.024,-0.08 0.131,0.013 0.277,0.013C0.295,0.013 0.312,0.013 0.329,0.008L0.388,-0.082C0.361,-0.065 0.334,-0.053 0.302,-0.053C0.197,-0.053 0.125,-0.142 0.125,-0.243C0.125,-0.334 0.19,-0.407 0.27,-0.407C0.323,-0.407 0.374,-0.383 0.399,-0.335C0.418,-0.298 0.415,-0.254 0.415,-0.214L0.415,-0L0.544,-0L0.544,-0.003C0.5,-0.027 0.506,-0.064 0.506,-0.11L0.506,-0.674L0.503,-0.674C0.492,-0.658 0.468,-0.658 0.445,-0.658L0.377,-0.658Z" style="fill:rgb(0,128,188);fill-rule:nonzero;"/>
</g>
<g transform="matrix(344.475,0,0,344.475,229.914,267.042)">
<path d="M0.5,-0.246C0.504,-0.375 0.411,-0.473 0.275,-0.473C0.126,-0.473 0.025,-0.372 0.025,-0.233C0.025,-0.094 0.142,0.013 0.312,0.013C0.359,0.013 0.407,0.006 0.45,-0.012L0.5,-0.106L0.497,-0.106C0.451,-0.07 0.393,-0.053 0.333,-0.053C0.22,-0.053 0.135,-0.124 0.133,-0.246L0.5,-0.246ZM0.137,-0.304C0.149,-0.367 0.199,-0.407 0.266,-0.407C0.338,-0.407 0.384,-0.374 0.395,-0.304L0.137,-0.304Z" style="fill:rgb(0,128,188);fill-rule:nonzero;"/>
</g>
<g transform="matrix(344.475,0,0,344.475,409.04,267.042)">
<path d="M0.023,-0.394L0.071,-0.394L0.071,-0.11C0.071,-0.064 0.077,-0.027 0.033,-0.003L0.033,-0L0.2,-0L0.2,-0.003C0.156,-0.028 0.162,-0.064 0.162,-0.11L0.162,-0.394L0.264,-0.394C0.276,-0.394 0.291,-0.391 0.295,-0.38L0.298,-0.38L0.298,-0.46L0.162,-0.46C0.162,-0.56 0.157,-0.608 0.249,-0.608C0.278,-0.608 0.308,-0.603 0.333,-0.59L0.333,-0.11C0.333,-0.064 0.339,-0.027 0.295,-0.003L0.295,-0L0.462,-0L0.462,-0.003C0.418,-0.027 0.424,-0.064 0.424,-0.11L0.424,-0.674L0.421,-0.674C0.411,-0.663 0.394,-0.656 0.378,-0.656C0.347,-0.656 0.319,-0.674 0.266,-0.674C0.206,-0.674 0.148,-0.654 0.107,-0.608C0.068,-0.564 0.071,-0.525 0.071,-0.46L0.023,-0.394Z" style="fill:rgb(86,86,86);fill-rule:nonzero;"/>
</g>
<g transform="matrix(344.475,0,0,344.475,581.278,267.042)">
<path d="M0.276,0.013C0.417,0.013 0.537,-0.091 0.537,-0.235C0.537,-0.303 0.506,-0.369 0.455,-0.414C0.407,-0.456 0.352,-0.473 0.288,-0.473C0.144,-0.473 0.023,-0.376 0.023,-0.226C0.023,-0.084 0.139,0.013 0.276,0.013ZM0.281,-0.053C0.179,-0.053 0.124,-0.152 0.124,-0.244C0.124,-0.334 0.184,-0.407 0.277,-0.407C0.384,-0.407 0.436,-0.311 0.436,-0.214C0.436,-0.124 0.373,-0.053 0.281,-0.053Z" style="fill:rgb(86,86,86);fill-rule:nonzero;"/>
</g>
<g transform="matrix(344.475,0,0,344.475,774.184,267.042)">
<path d="M0.415,-0.461C0.38,-0.469 0.343,-0.473 0.307,-0.473C0.156,-0.473 0.022,-0.39 0.022,-0.218C0.022,-0.088 0.142,0.013 0.296,0.013C0.34,0.013 0.386,0.009 0.428,-0.007L0.48,-0.102L0.477,-0.102C0.438,-0.073 0.382,-0.053 0.331,-0.053C0.22,-0.053 0.123,-0.129 0.123,-0.244C0.123,-0.339 0.193,-0.407 0.29,-0.407C0.335,-0.407 0.383,-0.391 0.412,-0.358L0.415,-0.358L0.415,-0.461Z" style="fill:rgb(86,86,86);fill-rule:nonzero;"/>
</g>
<g transform="matrix(344.475,0,0,344.475,932.642,267.042)">
<path d="M0.029,-0.658L0.029,-0.655C0.072,-0.63 0.066,-0.593 0.066,-0.547L0.066,-0.111C0.066,-0.065 0.072,-0.028 0.029,-0.003L0.029,-0L0.196,-0L0.196,-0.003C0.151,-0.028 0.157,-0.065 0.157,-0.111L0.157,-0.674L0.154,-0.674C0.141,-0.659 0.117,-0.658 0.095,-0.658L0.029,-0.658ZM0.324,-0.056C0.343,-0.029 0.368,-0 0.426,-0L0.504,-0C0.459,-0.028 0.429,-0.071 0.398,-0.112L0.276,-0.267L0.443,-0.46L0.291,-0.46L0.291,-0.457C0.301,-0.451 0.31,-0.442 0.31,-0.428C0.31,-0.403 0.274,-0.365 0.259,-0.348L0.176,-0.257L0.324,-0.056Z" style="fill:rgb(86,86,86);fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,7 +1,9 @@
Flock Map App
DeFlock
Built with Flutter.
Map public surveillance infrastructure with OpenStreetMap.
Offline areas, privacy-respecting, designed for OpenStreetMap camera tagging.
Built with Flutter. Offline-capable, privacy-respecting, designed for documenting cameras, ALPRs, and other surveillance devices in your community.
Part of the DeFlock initiative to make surveillance infrastructure transparent and searchable.
This text is loaded from assets/info.txt.

View File

@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Flock Map App</string>
<string>DeFlock</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>flock_map_app</string>
<string>deflockapp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -55,7 +55,7 @@
<string>None</string>
<key>CFBundleURLSchemes</key>
<array>
<string>flockmap</string>
<string>deflockapp</string>
</array>
</dict>
</array>

View File

@@ -25,7 +25,7 @@ const double kScaleBarBottomOffset = 170.0;
const double kAddPinYOffset = 0.0;
// Client name and version for OSM uploads ("created_by" tag)
const String kClientName = 'FlockMap';
const String kClientName = 'DeFlock';
const String kClientVersion = '0.9.7';
// Marker/node interaction

View File

@@ -30,22 +30,25 @@ Future<void> main() async {
),
);
}
return const FlockMapApp();
return const DeFlockApp();
},
),
),
);
}
class FlockMapApp extends StatelessWidget {
const FlockMapApp({super.key});
class DeFlockApp extends StatelessWidget {
const DeFlockApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flock Map',
title: 'DeFlock',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF0080BC), // DeFlock blue
brightness: Brightness.dark,
),
useMaterial3: true,
),
routes: {

View File

@@ -151,7 +151,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Flock Map'),
title: const Text('DeFlock'),
actions: [
IconButton(
tooltip: _getFollowMeTooltip(appState.followMeMode),

View File

@@ -13,7 +13,7 @@ import '../app_state.dart' show UploadMode;
class AuthService {
// Both client IDs from keys.dart
static const _redirect = 'flockmap://auth';
static const _redirect = 'deflockapp://auth';
late OAuth2Helper _helper;
String? _displayName;
@@ -46,7 +46,7 @@ class AuthService {
authorizeUrl: '$authBase/oauth2/authorize',
tokenUrl: '$authBase/oauth2/token',
redirectUri: _redirect,
customUriScheme: 'flockmap',
customUriScheme: 'deflockapp',
);
_helper = OAuth2Helper(
client,

View File

@@ -3,7 +3,7 @@ import 'dart:io';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';
import 'package:flock_map_app/dev_config.dart';
import 'package:deflockapp/dev_config.dart';
import '../network_status.dart';
/// Global semaphore to limit simultaneous tile fetches

View File

@@ -11,7 +11,7 @@ import 'offline_areas/world_area_manager.dart';
import '../models/osm_camera_node.dart';
import '../app_state.dart';
import 'map_data_provider.dart';
import 'package:flock_map_app/dev_config.dart';
import 'package:deflockapp/dev_config.dart';
/// Service for managing download, storage, and retrieval of offline map areas and cameras.
class OfflineAreaService {

View File

@@ -10,7 +10,7 @@ import '../../models/osm_camera_node.dart';
import '../map_data_provider.dart';
import 'offline_area_models.dart';
import 'offline_tile_utils.dart';
import 'package:flock_map_app/dev_config.dart';
import 'package:deflockapp/dev_config.dart';
/// Handles the actual downloading process for offline areas
class OfflineAreaDownloader {

View File

@@ -5,7 +5,7 @@ import 'package:path_provider/path_provider.dart';
import 'offline_area_models.dart';
import 'offline_tile_utils.dart';
import 'package:flock_map_app/dev_config.dart';
import 'package:deflockapp/dev_config.dart';
/// Manages the world area (permanent offline area for base map)
class WorldAreaManager {

View File

@@ -77,7 +77,7 @@ class TileLayerManager {
return TileLayer(
urlTemplate: urlTemplate,
userAgentPackageName: 'com.stopflock.flock_map_app',
userAgentPackageName: 'me.deflock.deflockapp',
tileProvider: NetworkTileProvider(
httpClient: _tileHttpClient,
// Enable flutter_map caching - cache busting handled by URL changes and FlutterMap key

View File

@@ -1,5 +1,5 @@
name: flock_map_app
description: Simple OSM cameramapping client
name: deflockapp
description: Map public surveillance infrastructure with OpenStreetMap
publish_to: "none"
version: 0.5.0

View File

@@ -1,8 +1,8 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:latlong2/latlong.dart';
import 'package:flock_map_app/models/pending_upload.dart';
import 'package:flock_map_app/models/camera_profile.dart';
import 'package:flock_map_app/state/settings_state.dart';
import 'package:deflockapp/models/pending_upload.dart';
import 'package:deflockapp/models/node_profile.dart';
import 'package:deflockapp/state/settings_state.dart';
void main() {
group('PendingUpload', () {
@@ -18,7 +18,7 @@ void main() {
final original = PendingUpload(
coord: LatLng(37.7749, -122.4194),
direction: 90.0,
profile: CameraProfile.flock(),
profile: NodeProfile.flock(),
uploadMode: mode,
);
@@ -46,7 +46,7 @@ void main() {
'lat': 37.7749,
'lon': -122.4194,
'dir': 90.0,
'profile': CameraProfile.flock().toJson(),
'profile': NodeProfile.flock().toJson(),
'originalNodeId': null,
'attempts': 0,
'error': false,
@@ -64,7 +64,7 @@ void main() {
final newCamera = PendingUpload(
coord: LatLng(37.7749, -122.4194),
direction: 90.0,
profile: CameraProfile.flock(),
profile: NodeProfile.flock(),
uploadMode: UploadMode.production,
);

View File

@@ -8,7 +8,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flock_map_app/main.dart';
import 'package:deflockapp/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {