From e472dc01f4052b7125a89040fe49a9b7abdd3920 Mon Sep 17 00:00:00 2001 From: joaquimcardeira Date: Mon, 15 Dec 2025 16:54:19 +0000 Subject: [PATCH 01/18] 3.3 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index a16e368..477017a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: manager description: "Track Fleet" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 3.2.0+1 +version: 3.3.0+1 environment: sdk: ^3.9.2 From dc762c62551ccfc9dca83dde111dbf24cc32e803 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Mon, 15 Dec 2025 17:59:05 +0000 Subject: [PATCH 02/18] 3.3.1 --- pubspec.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 477017a..19e5096 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: manager description: "Track Fleet" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 3.3.0+1 +version: 3.3.1+1 environment: sdk: ^3.9.2 @@ -26,6 +26,7 @@ dependencies: share_plus: ^12.0.1 firebase_core: ^4.2.1 firebase_crashlytics: ^5.0.5 + firebase_messaging: ^16.0.4 dev_dependencies: flutter_test: From b7f99cb66dd7fad1b6f9cc85218253e1f7436f7b Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Mon, 15 Dec 2025 18:52:07 +0000 Subject: [PATCH 03/18] 3.3.2 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 19e5096..38c6ba6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: manager description: "Track Fleet" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 3.3.1+1 +version: 3.3.2+1 environment: sdk: ^3.9.2 From 11676356ab2fee1fb88bcc64cc9f296b054cb4b3 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Mon, 15 Dec 2025 20:03:21 +0000 Subject: [PATCH 04/18] user icon --- assets/green_user_icon.svg | 33 +++++++++++++++++++++++++++++++++ assets/grey_user_icon.svg | 33 +++++++++++++++++++++++++++++++++ assets/red_user_icon.svg | 33 +++++++++++++++++++++++++++++++++ lib/utils/constants.dart | 2 +- lib/widgets/map_view.dart | 10 +++++++--- scripts/_generate_icons.sh | 4 ++++ 6 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 assets/green_user_icon.svg create mode 100644 assets/grey_user_icon.svg create mode 100644 assets/red_user_icon.svg diff --git a/assets/green_user_icon.svg b/assets/green_user_icon.svg new file mode 100644 index 0000000..63a1c05 --- /dev/null +++ b/assets/green_user_icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/assets/grey_user_icon.svg b/assets/grey_user_icon.svg new file mode 100644 index 0000000..c7b3ef9 --- /dev/null +++ b/assets/grey_user_icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/assets/red_user_icon.svg b/assets/red_user_icon.svg new file mode 100644 index 0000000..52935f3 --- /dev/null +++ b/assets/red_user_icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 959d31f..f63981a 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -1,5 +1,5 @@ const categoryIcons = [ - 'truck', 'car' + 'truck', 'car', 'person' ]; const colors = [ diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index cc88c38..42502ed 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -344,9 +344,13 @@ class _MapViewState extends State { Future addImageFromAsset(String name, String assetName) async { dev.log('adding $name, $assetName'); - final bytes = await rootBundle.load(assetName); - final list = bytes.buffer.asUint8List(); - return mapController!.addImage(name, list); + try { + final bytes = await rootBundle.load(assetName); + final list = bytes.buffer.asUint8List(); + return mapController!.addImage(name, list); + } catch (e) { + dev.log('$e'); + } } Future addImageFromIcon(String name, IconData icon, Color color, {double size = 48}) async { diff --git a/scripts/_generate_icons.sh b/scripts/_generate_icons.sh index 1da5dd3..da02826 100755 --- a/scripts/_generate_icons.sh +++ b/scripts/_generate_icons.sh @@ -101,3 +101,7 @@ for COLOR_SPEC in "${COLOR_ARRAY[@]}"; do fi echo " - ${OUTPUT_DIR}/${COLOR_NAME}/ (hex: $COLOR_HEX)" done + +rsvg-convert -w 50 -h 50 assets/red_user_icon.svg > "${OUTPUT_DIR}/person_red_000.0.png" +rsvg-convert -w 50 -h 50 assets/green_user_icon.svg > "${OUTPUT_DIR}/person_green_000.0.png" +rsvg-convert -w 50 -h 50 assets/grey_user_icon.svg > "${OUTPUT_DIR}/person_grey_000.0.png" From bf19a92f04f58ade514a2eae9040da84e6c16b8b Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Mon, 15 Dec 2025 20:23:58 +0000 Subject: [PATCH 05/18] selectedZoomLevel=14 --- lib/utils/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index f63981a..bc407c0 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -26,5 +26,5 @@ String get googleMapsClientId { return ''; } -const double selectedZoomLevel=15; +const double selectedZoomLevel=14; const int maxGeofences=100; From 613ba5b1c319db456b5747027aa291b2cbffcc14 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Mon, 15 Dec 2025 20:43:49 +0000 Subject: [PATCH 06/18] start notifications --- android/app/src/main/AndroidManifest.xml | 9 ++ ios/Runner.xcodeproj/project.pbxproj | 31 ++++++ ios/Runner/Runner.entitlements | 8 ++ lib/main.dart | 6 + lib/services/notification_service.dart | 136 +++++++++++++++++++++++ pubspec.lock | 24 ++++ 6 files changed, 214 insertions(+) create mode 100644 ios/Runner/Runner.entitlements create mode 100644 lib/services/notification_service.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9b60cb3..78b16a8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + @@ -36,6 +37,14 @@ + + + + diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..620e46e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/lib/main.dart b/lib/main.dart index d708ffb..a305a4d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,6 +19,11 @@ Future main() async { options: DefaultFirebaseOptions.currentPlatform, ); + // Initialize notifications + if (!kIsWeb) { + await NotificationService().initialize(); + } + FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; PlatformDispatcher.instance.onError = (error, stack) { diff --git a/lib/widgets/profile_view.dart b/lib/widgets/profile_view.dart index 732b4fc..6fbb088 100644 --- a/lib/widgets/profile_view.dart +++ b/lib/widgets/profile_view.dart @@ -1,6 +1,8 @@ +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import 'package:manager/l10n/app_localizations.dart'; import '../services/auth_service.dart'; +import '../services/notification_service.dart'; class ProfileView extends StatelessWidget { final int deviceCount; @@ -101,6 +103,21 @@ class ProfileView extends StatelessWidget { ), ), + // Test Notification Button (only on mobile) + if (!kIsWeb) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ElevatedButton.icon( + onPressed: () => _handleTestNotification(context), + icon: const Icon(Icons.notifications_active), + label: const Text('Test Notification'), + style: ElevatedButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + ), + ), + ), + const SizedBox(height: 8), + // Logout Button Padding( padding: const EdgeInsets.all(16), @@ -122,6 +139,29 @@ class ProfileView extends StatelessWidget { ); } + Future _handleTestNotification(BuildContext context) async { + try { + await NotificationService().showTestNotification(); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Test notification sent!'), + duration: Duration(seconds: 2), + ), + ); + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to send notification: $e'), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + } + } + Future _handleLogout(BuildContext context, AuthService authService) async { final l10n = AppLocalizations.of(context)!; final confirmed = await showDialog( diff --git a/pubspec.lock b/pubspec.lock index 6ff61e9..982a63d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -105,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" fake_async: dependency: transitive description: @@ -222,6 +230,38 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" + url: "https://pub.dev" + source: hosted + version: "19.5.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe" + url: "https://pub.dev" + source: hosted + version: "9.1.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf" + url: "https://pub.dev" + source: hosted + version: "1.0.3" flutter_localizations: dependency: "direct main" description: flutter @@ -594,6 +634,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7" + timezone: + dependency: transitive + description: + name: timezone + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 + url: "https://pub.dev" + source: hosted + version: "0.10.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 38c6ba6..a60d10f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: share_plus: ^12.0.1 firebase_core: ^4.2.1 firebase_crashlytics: ^5.0.5 - firebase_messaging: ^16.0.4 + flutter_local_notifications: ^19.0.1 dev_dependencies: flutter_test: From 2455fe3428eff510de24fd56cbf3e65f708bc8ae Mon Sep 17 00:00:00 2001 From: joaquimcardeira Date: Tue, 16 Dec 2025 18:43:47 +0000 Subject: [PATCH 08/18] Podfile --- ios/Podfile.lock | 161 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 ios/Podfile.lock diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..02c7be8 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,161 @@ +PODS: + - Firebase/CoreOnly (12.4.0): + - FirebaseCore (~> 12.4.0) + - Firebase/Crashlytics (12.4.0): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 12.4.0) + - firebase_core (4.2.1): + - Firebase/CoreOnly (= 12.4.0) + - Flutter + - firebase_crashlytics (5.0.5): + - Firebase/Crashlytics (= 12.4.0) + - firebase_core + - Flutter + - FirebaseCore (12.4.0): + - FirebaseCoreInternal (~> 12.4.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreInternal (12.4.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseCrashlytics (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - FirebaseRemoteConfigInterop (~> 12.4.0) + - FirebaseSessions (~> 12.4.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - FirebaseInstallations (12.4.0): + - FirebaseCore (~> 12.4.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseRemoteConfigInterop (12.4.0) + - FirebaseSessions (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreExtension (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesSwift (~> 2.1) + - Flutter (1.0.0) + - flutter_local_notifications (0.0.1): + - Flutter + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - MapLibre (6.19.1) + - maplibre_gl (0.24.1): + - Flutter + - MapLibre (= 6.19.1) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - PromisesObjC (2.4.0) + - PromisesSwift (2.4.0): + - PromisesObjC (= 2.4.0) + - share_plus (0.0.1): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) + - Flutter (from `Flutter`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseCrashlytics + - FirebaseInstallations + - FirebaseRemoteConfigInterop + - FirebaseSessions + - GoogleDataTransport + - GoogleUtilities + - MapLibre + - nanopb + - PromisesObjC + - PromisesSwift + +EXTERNAL SOURCES: + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_crashlytics: + :path: ".symlinks/plugins/firebase_crashlytics/ios" + Flutter: + :path: Flutter + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + maplibre_gl: + :path: ".symlinks/plugins/maplibre_gl/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e + firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 + firebase_crashlytics: c039028126cb45e32f4c217aa392408b0963d081 + FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 + FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018 + FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 + FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395 + FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2 + FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766 + FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + MapLibre: 7f24faba45439f80ccb0f83393c29fa32cb81952 + maplibre_gl: d83126f1b19adee5e1071c453b421efd5fc99883 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b + +PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e + +COCOAPODS: 1.16.2 From c53a2973b8185278e49bdc0689ef2c93362f3a53 Mon Sep 17 00:00:00 2001 From: joaquimcardeira Date: Tue, 16 Dec 2025 18:48:55 +0000 Subject: [PATCH 09/18] Podfile --- ios/Podfile.lock | 35 ++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 12 +---- lib/widgets/profile_view.dart | 80 ++++++++++++++++++++-------- pubspec.yaml | 1 + 4 files changed, 96 insertions(+), 32 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 02c7be8..6ef03b0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,6 +4,9 @@ PODS: - Firebase/Crashlytics (12.4.0): - Firebase/CoreOnly - FirebaseCrashlytics (~> 12.4.0) + - Firebase/Messaging (12.4.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 12.4.0) - firebase_core (4.2.1): - Firebase/CoreOnly (= 12.4.0) - Flutter @@ -11,6 +14,10 @@ PODS: - Firebase/Crashlytics (= 12.4.0) - firebase_core - Flutter + - firebase_messaging (16.0.4): + - Firebase/Messaging (= 12.4.0) + - firebase_core + - Flutter - FirebaseCore (12.4.0): - FirebaseCoreInternal (~> 12.4.0) - GoogleUtilities/Environment (~> 8.1) @@ -33,6 +40,15 @@ PODS: - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - PromisesObjC (~> 2.4) + - FirebaseMessaging (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Reachability (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) - FirebaseRemoteConfigInterop (12.4.0) - FirebaseSessions (12.4.0): - FirebaseCore (~> 12.4.0) @@ -49,14 +65,27 @@ PODS: - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy - GoogleUtilities/Environment (8.1.0): - GoogleUtilities/Privacy - GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability - "GoogleUtilities/NSData+zlib (8.1.0)": - GoogleUtilities/Privacy - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy - GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy @@ -86,6 +115,7 @@ PODS: DEPENDENCIES: - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) + - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) @@ -102,6 +132,7 @@ SPEC REPOS: - FirebaseCoreInternal - FirebaseCrashlytics - FirebaseInstallations + - FirebaseMessaging - FirebaseRemoteConfigInterop - FirebaseSessions - GoogleDataTransport @@ -116,6 +147,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_core/ios" firebase_crashlytics: :path: ".symlinks/plugins/firebase_crashlytics/ios" + firebase_messaging: + :path: ".symlinks/plugins/firebase_messaging/ios" Flutter: :path: Flutter flutter_local_notifications: @@ -135,11 +168,13 @@ SPEC CHECKSUMS: Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 firebase_crashlytics: c039028126cb45e32f4c217aa392408b0963d081 + firebase_messaging: c17a29984eafce4b2997fe078bb0a9e0b06f5dde FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018 FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395 FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2 + FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5 FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766 FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9395da7..55c8177 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -203,7 +203,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, A1EB2AD03057DF25EF1FCA9D /* [CP] Embed Pods Frameworks */, - B401872F13C07880E19FF0C9 /* [CP] Copy Pods Resources */, + B83303F3076ABB8BB851F36E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -315,20 +315,16 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - B401872F13C07880E19FF0C9 /* [CP] Copy Pods Resources */ = { + B83303F3076ABB8BB851F36E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -336,14 +332,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; diff --git a/lib/widgets/profile_view.dart b/lib/widgets/profile_view.dart index 6fbb088..2dc0a71 100644 --- a/lib/widgets/profile_view.dart +++ b/lib/widgets/profile_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:manager/l10n/app_localizations.dart'; import '../services/auth_service.dart'; import '../services/notification_service.dart'; @@ -103,14 +104,14 @@ class ProfileView extends StatelessWidget { ), ), - // Test Notification Button (only on mobile) + // Show FCM Token Button (only on mobile) if (!kIsWeb) Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ElevatedButton.icon( - onPressed: () => _handleTestNotification(context), + onPressed: () => _showFcmToken(context), icon: const Icon(Icons.notifications_active), - label: const Text('Test Notification'), + label: const Text('Show FCM Token'), style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), ), @@ -139,27 +140,62 @@ class ProfileView extends StatelessWidget { ); } - Future _handleTestNotification(BuildContext context) async { - try { - await NotificationService().showTestNotification(); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Test notification sent!'), - duration: Duration(seconds: 2), + void _showFcmToken(BuildContext context) { + final token = NotificationService().fcmToken; + + if (token == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('FCM token not available yet. Please try again in a moment.'), + duration: Duration(seconds: 2), + ), + ); + return; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('FCM Token'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Use this token to send test notifications from Firebase Console:', + style: TextStyle(fontSize: 12), + ), + const SizedBox(height: 12), + SelectableText( + token, + style: const TextStyle( + fontSize: 10, + fontFamily: 'monospace', + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: token)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Token copied to clipboard!'), + duration: Duration(seconds: 2), + ), + ); + Navigator.pop(context); + }, + child: const Text('Copy'), ), - ); - } - } catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Failed to send notification: $e'), - backgroundColor: Theme.of(context).colorScheme.error, + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Close'), ), - ); - } - } + ], + ), + ); } Future _handleLogout(BuildContext context, AuthService authService) async { diff --git a/pubspec.yaml b/pubspec.yaml index a60d10f..fea2dfc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: share_plus: ^12.0.1 firebase_core: ^4.2.1 firebase_crashlytics: ^5.0.5 + firebase_messaging: ^16.0.4 flutter_local_notifications: ^19.0.1 dev_dependencies: From 7294f7cccebd753d7f1f733b1c07d2b7e109487d Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Tue, 16 Dec 2025 18:51:21 +0000 Subject: [PATCH 10/18] version info --- lib/widgets/profile_view.dart | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/widgets/profile_view.dart b/lib/widgets/profile_view.dart index 2dc0a71..81eca04 100644 --- a/lib/widgets/profile_view.dart +++ b/lib/widgets/profile_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:manager/l10n/app_localizations.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import '../services/auth_service.dart'; import '../services/notification_service.dart'; @@ -133,6 +134,28 @@ class ProfileView extends StatelessWidget { ), ), ), + + // Version Information + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + if (snapshot.hasData) { + final info = snapshot.data!; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Center( + child: Text( + 'Version ${info.version} (${info.buildNumber})', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ); + } + return const SizedBox.shrink(); + }, + ), ], ), ); @@ -252,4 +275,4 @@ class ProfileView extends StatelessWidget { ], ); } -} \ No newline at end of file +} From 5664e824f665e0d3c99eb7103dca506fc8e17461 Mon Sep 17 00:00:00 2001 From: joaquimcardeira Date: Tue, 16 Dec 2025 20:40:08 +0000 Subject: [PATCH 11/18] package_info_plus --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index fea2dfc..4b60302 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: firebase_crashlytics: ^5.0.5 firebase_messaging: ^16.0.4 flutter_local_notifications: ^19.0.1 + package_info_plus: ^8.1.3 dev_dependencies: flutter_test: From eff5731c554db68fd982a1611f60d5bac20d50d8 Mon Sep 17 00:00:00 2001 From: joaquimcardeira Date: Tue, 16 Dec 2025 21:16:32 +0000 Subject: [PATCH 12/18] package_info_plus --- ios/Podfile.lock | 6 ++++++ pubspec.lock | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6ef03b0..cb45782 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -98,6 +98,8 @@ PODS: - nanopb/encode (= 3.30910.0) - nanopb/decode (3.30910.0) - nanopb/encode (3.30910.0) + - package_info_plus (0.4.5): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS @@ -119,6 +121,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -155,6 +158,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_local_notifications/ios" maplibre_gl: :path: ".symlinks/plugins/maplibre_gl/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" share_plus: @@ -184,6 +189,7 @@ SPEC CHECKSUMS: MapLibre: 7f24faba45439f80ccb0f83393c29fa32cb81952 maplibre_gl: d83126f1b19adee5e1071c453b421efd5fc99883 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 diff --git a/pubspec.lock b/pubspec.lock index 982a63d..ac39221 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -421,6 +421,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968" + url: "https://pub.dev" + source: hosted + version: "8.3.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" + url: "https://pub.dev" + source: hosted + version: "3.2.1" path: dependency: transitive description: From f526461797308ad961bc82f6d3e951d74af3692e Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Wed, 17 Dec 2025 16:05:42 +0000 Subject: [PATCH 13/18] filter invalid positions --- lib/services/api_service.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 2cf1471..225c81d 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -56,10 +56,12 @@ class ApiService { } Future> fetchPositions() async { - return _fetchList( + final positions = await _fetchList( endpoint: '/api/positions', fromJson: Position.fromJson ); + // Filter out invalid positions + return positions.where((position) => position.valid).toList(); } Future> fetchGeofences() async { From 0fcb25a5a7335995dd53d5e155f1bd1ba12521ec Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Wed, 17 Dec 2025 16:47:42 +0000 Subject: [PATCH 14/18] filter invalid positions --- lib/services/api_service.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 225c81d..e33d520 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -105,10 +105,12 @@ class ApiService { }) async { final fromParam = from.toUtc().toIso8601String(); final toParam = to.toUtc().toIso8601String(); - return _fetchList( + final positions = await _fetchList( endpoint: '/api/reports/route?deviceId=$deviceId&from=$fromParam&to=$toParam', fromJson: Position.fromJson ); + // Filter out invalid positions + return positions.where((position) => position.valid).toList(); } Future> fetchTrips({ From a1db042bf8425339403d92df0f9f5acc02d13818 Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Wed, 17 Dec 2025 17:00:40 +0000 Subject: [PATCH 15/18] optimize for airtags --- lib/widgets/device_route.dart | 27 +++++++++++++++++++++++++++ lib/widgets/map_view.dart | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/lib/widgets/device_route.dart b/lib/widgets/device_route.dart index 309094f..47bc79f 100644 --- a/lib/widgets/device_route.dart +++ b/lib/widgets/device_route.dart @@ -159,6 +159,30 @@ class _DeviceRouteState extends State { if (_positions.isEmpty) return items; + // For AirTags, just list different positions without trip/stop processing + if (widget.device.model?.toLowerCase() == 'airtag') { + // Filter to only include positions with different coordinates + final differentPositions = []; + for (int i = 0; i < _positions.length; i++) { + if (i == 0 || + _positions[i].latitude != _positions[i - 1].latitude || + _positions[i].longitude != _positions[i - 1].longitude) { + differentPositions.add(_positions[i]); + } + } + + // Add filtered positions to items + for (int i = 0; i < differentPositions.length; i++) { + final isLastPosition = i == differentPositions.length - 1; + items.add(_PositionItem( + differentPositions[i], + isFirst: i == 0, + label: (!isLastPosition && i > 0) ? 'AirTag Location' : null, + )); + } + return items; + } + // Add first position items.add(_PositionItem(_positions.first, isFirst: true)); @@ -675,6 +699,9 @@ class _PositionCard extends StatelessWidget { } else if (label == 'Stop') { icon = Icons.stop_circle; iconColor = colors.error; + } else if (label == 'AirTag Location') { + icon = Icons.location_on; + iconColor = colors.primary; } else if (isFirst) { icon = Icons.flag; iconColor = colors.tertiary; diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 42502ed..91cca8f 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -253,6 +253,10 @@ class _MapViewState extends State { icon = Icons.stop_circle; iconColor = colors.error; iconName = 'position-stop'; + } else if (widget.positionLabel == 'AirTag Location') { + icon = Icons.location_on; + iconColor = colors.primary; + iconName = 'position-airtag-location'; } else { // Day start/end positions final isFirst = widget.isFirstPosition ?? true; From d31174c3d86af6b26a55a48d45968fa14726663e Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Thu, 18 Dec 2025 19:31:36 +0000 Subject: [PATCH 16/18] zoom in/out --- lib/widgets/map/style_selector.dart | 61 ++++++++++++++++++++++++++++- lib/widgets/map_view.dart | 18 +++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/lib/widgets/map/style_selector.dart b/lib/widgets/map/style_selector.dart index 6dcac9d..ace5c91 100644 --- a/lib/widgets/map/style_selector.dart +++ b/lib/widgets/map/style_selector.dart @@ -8,6 +8,8 @@ class MapStyleSelector extends StatefulWidget { final Function(int) onStyleSelected; final bool geofencesLayer; final Function() onLayerSelected; + final Function() onZoomIn; + final Function() onZoomOut; const MapStyleSelector({ super.key, @@ -16,6 +18,8 @@ class MapStyleSelector extends StatefulWidget { required this.onStyleSelected, required this.geofencesLayer, required this.onLayerSelected, + required this.onZoomIn, + required this.onZoomOut, }); @override @@ -30,7 +34,7 @@ class _MapStyleSelectorState extends State { final l10n = AppLocalizations.of(context)!; return Positioned( - top: 60, + top: 0, right: 0, child: SafeArea( child: AnimatedSize( @@ -65,6 +69,59 @@ class _MapStyleSelectorState extends State { if (_menuExpanded) ...[ const Divider(height: 1), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + onTap: widget.onZoomIn, + borderRadius: BorderRadius.circular(20), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Theme.of(context).colorScheme.outline, + ), + ), + child: Icon( + Icons.add, + size: 20, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + const SizedBox(width: 12), + InkWell( + onTap: widget.onZoomOut, + borderRadius: BorderRadius.circular(20), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Theme.of(context).colorScheme.outline, + ), + ), + child: Icon( + Icons.remove, + size: 20, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + ), + + const Divider(height: 1), + ...List.generate(MapStyles.configs.length, (index) { final config = MapStyles.configs[index]; final isSelected = widget.selectedStyleIndex == index; @@ -135,7 +192,7 @@ class _MapStyleSelectorState extends State { ], ), ), - ) + ), ], ], ), diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 91cca8f..22a05ff 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -99,6 +99,22 @@ class _MapViewState extends State { await mapController!.setLayerVisibility(MapStyles.geofenceLabelLayerId, _geofencesSelected); } + Future _zoomIn() async { + if (mapController == null) return; + await mapController!.animateCamera( + CameraUpdate.zoomIn(), + duration: const Duration(milliseconds: 200), + ); + } + + Future _zoomOut() async { + if (mapController == null) return; + await mapController!.animateCamera( + CameraUpdate.zoomOut(), + duration: const Duration(milliseconds: 200), + ); + } + @override void didUpdateWidget(MapView oldWidget) { super.didUpdateWidget(oldWidget); @@ -845,6 +861,8 @@ class _MapViewState extends State { onStyleSelected: _applyStyle, geofencesLayer: _geofencesSelected, onLayerSelected: _layerSelected, + onZoomIn: _zoomIn, + onZoomOut: _zoomOut, ) ], ); From 12fe8a6296feda92f17c34ce673d4c59ee44a20a Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Fri, 19 Dec 2025 01:56:55 +0000 Subject: [PATCH 17/18] webview --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 4b60302..1ffb7e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: firebase_messaging: ^16.0.4 flutter_local_notifications: ^19.0.1 package_info_plus: ^8.1.3 + webview_flutter: ^4.13.0 dev_dependencies: flutter_test: From 1e411721680cf4a03e81e9b7bc72f27f90d34e6b Mon Sep 17 00:00:00 2001 From: Joaquim Cardeira Date: Wed, 28 Jan 2026 20:47:00 +0000 Subject: [PATCH 18/18] wuizy --- android/app/google-services.json | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/android/app/google-services.json b/android/app/google-services.json index f50b47d..70f010c 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -192,6 +192,42 @@ ] } } + }, + { + "client_info": { + "mobilesdk_app_id": "1:218052201497:android:a5188541e76de06f766e88", + "android_client_info": { + "package_name": "com.fleetmap.wuizy" + } + }, + "oauth_client": [ + { + "client_id": "218052201497-73igrj6hlp8jkrso1sim49q4kodp61m0.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBVLNLhmrau9rbXv-e9yKC8qftQrR1MJ9Q" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "218052201497-73igrj6hlp8jkrso1sim49q4kodp61m0.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "218052201497-27fuda922siafb8uql7m1rirk9jrhtu3.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.fleetmap.manager" + } + } + ] + } + } } ], "configuration_version": "1"