From 44ec3837442679fd3f18352694cbd13b91b2534e Mon Sep 17 00:00:00 2001 From: rzuasti Date: Mon, 8 Jun 2026 14:46:55 -0400 Subject: [PATCH] Wire app-side Firebase push notifications - Rename Android package com.example.frontend -> net.oottsecurity.app (namespace, applicationId, MainActivity), and enable core library desugaring required by flutter_local_notifications - Configure Firebase from a committed lib/firebase_options.dart (client identifiers, not secrets) instead of native config files; initialize the push service with explicit options - iOS: add Runner.entitlements (aps-environment) wired via CODE_SIGN_ENTITLEMENTS for the Runner target, and UIBackgroundModes remote-notification in Info.plist - gitignore the unused native Firebase config files defensively dart analyze clean; 145 frontend tests pass. Co-Authored-By: Claude Opus 4.8 --- frontend/.gitignore | 8 +++ frontend/android/app/build.gradle.kts | 13 +++-- .../oottsecurity/app}/MainActivity.kt | 2 +- frontend/ios/Runner.xcodeproj/project.pbxproj | 3 ++ frontend/ios/Runner/Info.plist | 4 ++ frontend/ios/Runner/Runner.entitlements | 8 +++ frontend/lib/firebase_options.dart | 52 +++++++++++++++++++ frontend/lib/utils/push_service.dart | 10 ++-- 8 files changed, 93 insertions(+), 7 deletions(-) rename frontend/android/app/src/main/kotlin/{com/example/frontend => net/oottsecurity/app}/MainActivity.kt (76%) create mode 100644 frontend/ios/Runner/Runner.entitlements create mode 100644 frontend/lib/firebase_options.dart diff --git a/frontend/.gitignore b/frontend/.gitignore index 3820a95..4a0a37c 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -43,3 +43,11 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Firebase native config files. OOTT configures Firebase from the committed +# lib/firebase_options.dart instead, so these are not used by the build; ignored +# defensively so a stray download (e.g. from `flutterfire configure`) is not +# accidentally committed. +/android/app/google-services.json +/ios/Runner/GoogleService-Info.plist +/macos/Runner/GoogleService-Info.plist diff --git a/frontend/android/app/build.gradle.kts b/frontend/android/app/build.gradle.kts index b0933e4..a0909c8 100644 --- a/frontend/android/app/build.gradle.kts +++ b/frontend/android/app/build.gradle.kts @@ -6,13 +6,15 @@ plugins { } android { - namespace = "com.example.frontend" + namespace = "net.oottsecurity.app" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + // flutter_local_notifications uses java.time APIs that need desugaring on older Android. + isCoreLibraryDesugaringEnabled = true } kotlinOptions { @@ -20,8 +22,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.frontend" + applicationId = "net.oottsecurity.app" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion @@ -42,3 +43,9 @@ android { flutter { source = "../.." } + +dependencies { + // Backport of java.time etc. required by flutter_local_notifications (see + // isCoreLibraryDesugaringEnabled above). + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} diff --git a/frontend/android/app/src/main/kotlin/com/example/frontend/MainActivity.kt b/frontend/android/app/src/main/kotlin/net/oottsecurity/app/MainActivity.kt similarity index 76% rename from frontend/android/app/src/main/kotlin/com/example/frontend/MainActivity.kt rename to frontend/android/app/src/main/kotlin/net/oottsecurity/app/MainActivity.kt index 1bd2dee..3824ec0 100644 --- a/frontend/android/app/src/main/kotlin/com/example/frontend/MainActivity.kt +++ b/frontend/android/app/src/main/kotlin/net/oottsecurity/app/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.frontend +package net.oottsecurity.app import io.flutter.embedding.android.FlutterActivity diff --git a/frontend/ios/Runner.xcodeproj/project.pbxproj b/frontend/ios/Runner.xcodeproj/project.pbxproj index ade41ec..84ec1b6 100644 --- a/frontend/ios/Runner.xcodeproj/project.pbxproj +++ b/frontend/ios/Runner.xcodeproj/project.pbxproj @@ -368,6 +368,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; PRODUCT_BUNDLE_IDENTIFIER = "net.oott-security.app"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -547,6 +548,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; PRODUCT_BUNDLE_IDENTIFIER = "net.oott-security.app"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -569,6 +571,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; PRODUCT_BUNDLE_IDENTIFIER = "net.oott-security.app"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/frontend/ios/Runner/Info.plist b/frontend/ios/Runner/Info.plist index 0e44bf7..f6a3bce 100644 --- a/frontend/ios/Runner/Info.plist +++ b/frontend/ios/Runner/Info.plist @@ -56,5 +56,9 @@ ITSAppUsesNonExemptEncryption + UIBackgroundModes + + remote-notification + diff --git a/frontend/ios/Runner/Runner.entitlements b/frontend/ios/Runner/Runner.entitlements new file mode 100644 index 0000000..28c29bf --- /dev/null +++ b/frontend/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + production + + diff --git a/frontend/lib/firebase_options.dart b/frontend/lib/firebase_options.dart new file mode 100644 index 0000000..9b3b381 --- /dev/null +++ b/frontend/lib/firebase_options.dart @@ -0,0 +1,52 @@ +// Firebase client configuration for the OOTT app, used by push notifications. +// +// These values are client identifiers (project id, sender id, app id, and a +// client API key). They are not secrets: they ship inside every distributed app +// binary and are fully extractable. Sending pushes requires the relay's +// server-side credential (which never leaves Google), and relay abuse is bounded +// by FCM project scoping + per-IP rate limiting + the billing cap. See +// push_notifications.md. +// +// Mirrors the shape FlutterFire's `flutterfire configure` generates, but only +// the platforms OOTT delivers push to (Android, iOS) are configured. +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show TargetPlatform, defaultTargetPlatform, kIsWeb; + +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'Push notifications are not configured for web; this should never be ' + 'called there (see PushService.isSupported).', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not configured for $defaultTargetPlatform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyD6R22FPkZarDIZymszuzASDCjvIONts1U', + appId: '1:607706370042:android:39c9d29bfcdd385cf4de43', + messagingSenderId: '607706370042', + projectId: 'oott-push', + storageBucket: 'oott-push.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyCvDyPJJYW8JLKBdxdlCGsT-Y2nWfv7Gzs', + appId: '1:607706370042:ios:43768238f07348f8f4de43', + messagingSenderId: '607706370042', + projectId: 'oott-push', + storageBucket: 'oott-push.firebasestorage.app', + iosBundleId: 'net.oott-security.app', + ); +} diff --git a/frontend/lib/utils/push_service.dart b/frontend/lib/utils/push_service.dart index a331915..d49b0fe 100644 --- a/frontend/lib/utils/push_service.dart +++ b/frontend/lib/utils/push_service.dart @@ -3,6 +3,7 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import '../firebase_options.dart'; import 'oott_api.dart'; /// Per-device push enable/disable, behind an interface so the settings UI can be @@ -55,10 +56,13 @@ class FirebasePushService implements PushService { defaultTargetPlatform == TargetPlatform.iOS ? 'ios' : 'android'; Future _ensureFirebase() async { - // No options passed: the native google-services.json / GoogleService-Info - // .plist added during the one-time project setup provide them. + // Options come from the committed firebase_options.dart rather than native + // config files, so no google-services.json / GoogleService-Info.plist is + // needed in the build. if (Firebase.apps.isEmpty) { - await Firebase.initializeApp(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); } }