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 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-06-08 14:46:55 -04:00
co-authored by Claude Opus 4.8
parent d81eba3b37
commit 44ec383744
8 changed files with 93 additions and 7 deletions
+8
View File
@@ -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
+10 -3
View File
@@ -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")
}
@@ -1,4 +1,4 @@
package com.example.frontend
package net.oottsecurity.app
import io.flutter.embedding.android.FlutterActivity
@@ -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";
+4
View File
@@ -56,5 +56,9 @@
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
</dict>
</plist>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>production</string>
</dict>
</plist>
+52
View File
@@ -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',
);
}
+7 -3
View File
@@ -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<void> _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,
);
}
}