Skip to content

[firebase_messaging] Added dynamic firebase api configuration #2098

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
4 changes: 4 additions & 0 deletions packages/firebase_messaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 6.1.0

* Add the `FirebaseOptions options` parameter to the `FirebaseMessaging.configure()` call to set the firebase configuration at runtime.

## 6.0.14

* Fix for missing UserAgent.h compilation failures.
Expand Down
52 changes: 52 additions & 0 deletions packages/firebase_messaging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,58 @@ Future<Map<String, dynamic>> sendAndRetrieveMessage() async {
}
```

## Advanced usage

### Dynamic firebase configuration
If you need to configure firebase at runtime (instead of using `google-service.json` / `GoogleService-Info.plist`)
you can pass an options object to the configure call:
```dart
import 'dart:io' show Platform;
final firebase = FirebaseMessaging();

var options = Platform.isIOS
? FirebaseOptions( // iOS options
clientId: "...",
apiKey: "...",
gcmSenderId: "...",
bundleId: "...",
projectId: "...",
storageBucket: "...",
googleAppId: "...",
databaseUrl: "...",
)
: FirebaseOptions( // Android options
clientId: "...",
apiKey: "...",
gcmSenderId: "...",
projectId: "...",
storageBucket: "...",
googleAppId: "...",
databaseUrl: "...",
);

firebase.configure(
options: options,
);
```

### On Android:
You need to disable the `FirebaseInitProvider`.
To do this, put this into your `AndroidManifest.xml` inside the `<application>` tag:
```xml
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
tools:node="remove" />
```

To hide the error message `Missing google_app_id. Firebase Analytics disabled.` put the following lines into your `res/strings.xml` of your app:
```xml
<!-- Fake ID to hide the Firebase Analytics Error Message -->
<string name="google_app_id">1:0000000000000:android:0000000000000000</string>
```


## Issues and feedback

Please file Flutterfire specific issues, bugs, or feature requests in our [issue tracker](https://github.com/FirebaseExtended/flutterfire/issues/new).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import com.google.firebase.messaging.FirebaseMessaging;
Expand Down Expand Up @@ -44,6 +45,7 @@ public class FirebaseMessagingPlugin extends BroadcastReceiver
private MethodChannel channel;
private Context applicationContext;
private Activity mainActivity;
private boolean isInitialized = false;

public static void registerWith(Registrar registrar) {
FirebaseMessagingPlugin instance = new FirebaseMessagingPlugin();
Expand All @@ -54,7 +56,6 @@ public static void registerWith(Registrar registrar) {

private void onAttachedToEngine(Context context, BinaryMessenger binaryMessenger) {
this.applicationContext = context;
FirebaseApp.initializeApp(applicationContext);
channel = new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging");
final MethodChannel backgroundCallbackChannel =
new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging_background");
Expand Down Expand Up @@ -181,6 +182,37 @@ public void onMethodCall(final MethodCall call, final Result result) {
FlutterFirebaseMessagingService.onInitialized();
result.success(true);
} else if ("configure".equals(call.method)) {
if (!isInitialized) {
if (call.arguments instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, String> arguments = ((Map<String, String>) call.arguments);

String apiKey, googleAppId;
if ((apiKey = arguments.get("apiKey")) == null) {
result.error("apiKey", "apiKey must not be empty!", null);
return;
}
if ((googleAppId = arguments.get("googleAppId")) == null) {
result.error("googleAppId", "googleAppId must not be empty!", null);
return;
}

FirebaseOptions options =
new FirebaseOptions.Builder()
.setApiKey(apiKey)
.setGcmSenderId(arguments.get("gcmSenderId"))
.setProjectId(arguments.get("projectId"))
.setStorageBucket(arguments.get("storageBucket"))
.setApplicationId(googleAppId)
.setDatabaseUrl(arguments.get("databaseUrl"))
.setGaTrackingId(arguments.get("gaTrackingId"))
.build();
FirebaseApp.initializeApp(applicationContext, options);
} else {
FirebaseApp.initializeApp(applicationContext);
}
isInitialized = true;
}
FirebaseInstanceId.getInstance()
.getInstanceId()
.addOnCompleteListener(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ - (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
if (self) {
_channel = channel;
_resumingFromBackground = NO;
if (![FIRApp appNamed:@"__FIRAPP_DEFAULT"]) {
NSLog(@"Configuring the default Firebase app...");
[FIRApp configure];
NSLog(@"Configured the default Firebase app %@.", [FIRApp defaultApp].name);
}
[FIRMessaging messaging].delegate = self;
}
return self;
}
Expand Down Expand Up @@ -138,6 +132,56 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
result([NSNumber numberWithBool:YES]);
}
} else if ([@"configure" isEqualToString:method]) {
if (![FIRApp appNamed:@"__FIRAPP_DEFAULT"]) {
NSLog(@"Configuring the default Firebase app...");

if (call.arguments != nil && [call.arguments isKindOfClass:[NSDictionary class]]) {
NSDictionary *arguments = call.arguments;
NSString *googleAppId, *gcmSenderId, *clientId, *apiKey, *bundleId, *projectId,
*storageBucket, *databaseUrl;

if ((googleAppId = [arguments objectForKey:@"googleAppId"]) == nil) {
result([FlutterError errorWithCode:@"googleAppId"
message:@"googleAppId must not be empty"
details:nil]);
return;
}
if ((gcmSenderId = [arguments objectForKey:@"gcmSenderId"]) == nil) {
result([FlutterError errorWithCode:@"gcmSenderId"
message:@"gcmSenderId must not be empty"
details:nil]);
return;
}

FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:googleAppId
GCMSenderID:gcmSenderId];

if ((clientId = [arguments objectForKey:@"clientId"]) == nil) {
[options setClientID:clientId];
}
if ((apiKey = [arguments objectForKey:@"apiKey"]) != nil) {
[options setAPIKey:apiKey];
}
if ((bundleId = [arguments objectForKey:@"bundleId"]) != nil) {
[options setBundleID:bundleId];
}
if ((projectId = [arguments objectForKey:@"projectId"]) != nil) {
[options setProjectID:projectId];
}
if ((storageBucket = [arguments objectForKey:@"storageBucket"]) != nil) {
[options setStorageBucket:storageBucket];
}
if ((databaseUrl = [arguments objectForKey:@"databaseUrl"]) != nil) {
[options setDatabaseURL:databaseUrl];
}
[FIRApp configureWithOptions:options];
} else {
[FIRApp configure];
}
[FIRMessaging messaging].delegate = self;
NSLog(@"Configured the default Firebase app %@.", [FIRApp defaultApp].name);
}

[FIRMessaging messaging].shouldEstablishDirectChannel = true;
[[UIApplication sharedApplication] registerForRemoteNotifications];
if (_launchNotification != nil && _launchNotification[kGCMMessageIDKey]) {
Expand Down
42 changes: 41 additions & 1 deletion packages/firebase_messaging/lib/firebase_messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class FirebaseMessaging {
MessageHandler _onBackgroundMessage;
MessageHandler _onLaunch;
MessageHandler _onResume;
FirebaseOptions _options;

/// On iOS, prompts the user for notification permissions the first time
/// it is called.
Expand Down Expand Up @@ -106,12 +107,14 @@ class FirebaseMessaging {
MessageHandler onBackgroundMessage,
MessageHandler onLaunch,
MessageHandler onResume,
FirebaseOptions options,
}) {
_onMessage = onMessage;
_onLaunch = onLaunch;
_onResume = onResume;
_options = options;
_channel.setMethodCallHandler(_handleMethod);
_channel.invokeMethod<void>('configure');
_channel.invokeMethod<void>('configure', _options?.asMap());
if (onBackgroundMessage != null) {
_onBackgroundMessage = onBackgroundMessage;
final CallbackHandle backgroundSetupHandle =
Expand Down Expand Up @@ -236,3 +239,40 @@ class IosNotificationSettings {
@override
String toString() => 'PushNotificationSettings ${toMap()}';
}

class FirebaseOptions {
final String clientId;
final String apiKey;
final String gcmSenderId;
final String bundleId;
final String projectId;
final String storageBucket;
final String googleAppId;
final String databaseUrl;

/// Creates a new [FirebaseOptions] object
FirebaseOptions({
this.clientId,
this.apiKey,
this.gcmSenderId,
this.bundleId,
this.projectId,
this.storageBucket,
this.googleAppId,
this.databaseUrl,
});

/// Converts the options to a map for passing it through the platform channel
Map<String, String> asMap() {
return {
"clientId": clientId,
"apiKey": apiKey,
"gcmSenderId": gcmSenderId,
"bundleId": bundleId,
"projectId": projectId,
"storageBucket": storageBucket,
"googleAppId": googleAppId,
"databaseUrl": databaseUrl,
};
}
}
3 changes: 2 additions & 1 deletion packages/firebase_messaging/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: firebase_messaging
description: Flutter plugin for Firebase Cloud Messaging, a cross-platform
messaging solution that lets you reliably deliver messages on Android and iOS.
homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_messaging
version: 6.0.14

version: 6.1.0

flutter:
plugin:
Expand Down
20 changes: 19 additions & 1 deletion packages/firebase_messaging/test/firebase_messaging_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart' show TestWidgetsFlutterBinding;
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
Expand Down Expand Up @@ -68,6 +68,24 @@ void main() {
verify(mockChannel.invokeMethod<void>('configure'));
});

test('configure with options', () {
var firebaseOptions = FirebaseOptions(
clientId: "1",
apiKey: "2",
gcmSenderId: "3",
bundleId: "4",
projectId: "5",
storageBucket: "6",
googleAppId: "7",
databaseUrl: "8",
);
firebaseMessaging.configure(options: firebaseOptions);
verify(mockChannel.setMethodCallHandler(any));
verify(
mockChannel.invokeMethod<void>('configure', firebaseOptions.asMap()),
);
});

test('incoming token', () async {
firebaseMessaging.configure();
final dynamic handler =
Expand Down