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,
+ );
}
}