Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
<!-- Notification permission for proximity alerts -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<!-- BLE permissions for FlockSquawk scanner -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

<application
android:name="${applicationName}"
android:label="DeFlock"
Expand Down
6 changes: 6 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@
<array>
<string>https</string>
</array>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>DeFlock uses Bluetooth to receive detection data from FlockSquawk.</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
<key>UIStatusBarHidden</key>
<false/>
</dict>
Expand Down
40 changes: 35 additions & 5 deletions lib/app_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ import 'state/session_state.dart';
import 'state/settings_state.dart';
import 'state/suspected_location_state.dart';
import 'state/upload_queue_state.dart';
import 'state/scanner_state.dart';
import 'services/scanner_service.dart' show ScannerConnectionStatus, ScannerTransportType;

// Re-export types
export 'state/navigation_state.dart' show AppNavigationMode;
export 'state/settings_state.dart' show UploadMode, FollowMeMode;
export 'state/session_state.dart' show AddNodeSession, EditNodeSession;
export 'models/pending_upload.dart' show UploadOperation;
export 'state/scanner_state.dart' show ScannerState;
export 'services/scanner_service.dart' show ScannerConnectionStatus, ScannerTransportType;

// ------------------ AppState ------------------
class AppState extends ChangeNotifier {
Expand All @@ -56,6 +60,7 @@ class AppState extends ChangeNotifier {
late final SettingsState _settingsState;
late final SuspectedLocationState _suspectedLocationState;
late final UploadQueueState _uploadQueueState;
late final ScannerState _scannerState;

bool _isInitialized = false;

Expand All @@ -76,7 +81,8 @@ class AppState extends ChangeNotifier {
_settingsState = SettingsState();
_suspectedLocationState = SuspectedLocationState();
_uploadQueueState = UploadQueueState();

_scannerState = ScannerState();

// Set up state change listeners
_authState.addListener(_onStateChanged);
_messagesState.addListener(_onStateChanged);
Expand All @@ -88,7 +94,8 @@ class AppState extends ChangeNotifier {
_settingsState.addListener(_onStateChanged);
_suspectedLocationState.addListener(_onStateChanged);
_uploadQueueState.addListener(_onStateChanged);

_scannerState.addListener(_onStateChanged);

_init();
}

Expand Down Expand Up @@ -185,6 +192,13 @@ class AppState extends ChangeNotifier {
double? get suspectedLocationsDownloadProgress => _suspectedLocationState.downloadProgress;
Future<DateTime?> get suspectedLocationsLastFetch => _suspectedLocationState.lastFetchTime;

// Scanner state
ScannerConnectionStatus get scannerConnectionStatus => _scannerState.connectionStatus;
bool get isScannerConnected => _scannerState.isConnected;
int get scannerDetectionCount => _scannerState.detectionCount;
ScannerTransportType get scannerTransportType => _scannerState.activeTransportType;
ScannerState get scannerState => _scannerState;

void _onStateChanged() {
notifyListeners();
}
Expand Down Expand Up @@ -224,6 +238,7 @@ class AppState extends ChangeNotifier {
}

await _suspectedLocationState.init(offlineMode: _settingsState.offlineMode);
await _scannerState.init();
await _uploadQueueState.init();
await _authState.init(_settingsState.uploadMode);

Expand Down Expand Up @@ -829,19 +844,32 @@ class AppState extends ChangeNotifier {
final profileName = profile?.name.startsWith('<') == true && profile?.name.endsWith('>') == true
? 'a'
: profile?.name ?? 'surveillance';

switch (operation) {
case UploadOperation.create:
return 'Add $profileName surveillance node';
case UploadOperation.modify:
return 'Update $profileName surveillance node';
return 'Update $profileName surveillance node';
case UploadOperation.delete:
return 'Delete $profileName surveillance node';
case UploadOperation.extract:
return 'Extract $profileName surveillance node';
}
}

// ---------- Scanner Methods ----------
Future<bool> reconnectScanner() async {
return await _scannerState.reconnect();
}

Future<void> disconnectScanner() async {
await _scannerState.disconnect();
}

Future<void> linkDetectionToNode(String mac, int osmNodeId) async {
await _scannerState.linkDetectionToNode(mac, osmNodeId);
}

// ---------- Private Methods ----------
/// Attempts to fetch missing tile preview images in the background (fire and forget)
void _fetchMissingTilePreviews() {
Expand Down Expand Up @@ -874,7 +902,9 @@ class AppState extends ChangeNotifier {
_settingsState.removeListener(_onStateChanged);
_suspectedLocationState.removeListener(_onStateChanged);
_uploadQueueState.removeListener(_onStateChanged);

_scannerState.removeListener(_onStateChanged);

_scannerState.dispose();
_uploadQueueState.dispose();
super.dispose();
}
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'screens/about_screen.dart';
import 'screens/release_notes_screen.dart';
import 'screens/osm_account_screen.dart';
import 'screens/upload_queue_screen.dart';
import 'screens/scanner_screen.dart';
import 'services/localization_service.dart';
import 'services/version_service.dart';
import 'services/deep_link_service.dart';
Expand Down Expand Up @@ -86,6 +87,7 @@ class DeFlockApp extends StatelessWidget {
'/settings/language': (context) => const LanguageSettingsScreen(),
'/settings/about': (context) => const AboutScreen(),
'/settings/release-notes': (context) => const ReleaseNotesScreen(),
'/scanner': (context) => const ScannerScreen(),
},
initialRoute: '/',

Expand Down
Loading
Loading