Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.firebase.core.FlutterFirebasePlugin;
import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry;
import io.flutter.plugins.firebase.installations.GeneratedAndroidFirebaseAppInstallations;
import io.flutter.plugins.firebase.installations.GeneratedAndroidFirebaseAppInstallations.AppInstallationsPigeonFirebaseApp;
import io.flutter.plugins.firebase.installations.GeneratedAndroidFirebaseAppInstallations.AppInstallationsPigeonSettings;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/** FirebaseInstallationsPlugin */
public class FirebaseInstallationsPlugin
implements FlutterFirebasePlugin, FlutterPlugin, MethodCallHandler {
implements FlutterFirebasePlugin,
FlutterPlugin,
MethodCallHandler,
GeneratedAndroidFirebaseAppInstallations.FirebaseAppInstallationsHostApi {
private MethodChannel channel;
private static final String METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_app_installations";
private final Map<EventChannel, EventChannel.StreamHandler> streamHandlers = new HashMap<>();
Expand All @@ -38,6 +44,9 @@ private MethodChannel setup(BinaryMessenger binaryMessenger) {
final MethodChannel channel = new MethodChannel(binaryMessenger, METHOD_CHANNEL_NAME);
channel.setMethodCallHandler(this);
this.messenger = binaryMessenger;
// Set up Pigeon host API handlers.
GeneratedAndroidFirebaseAppInstallations.FirebaseAppInstallationsHostApi.setUp(
binaryMessenger, this);
return channel;
}

Expand All @@ -64,6 +73,12 @@ private FirebaseInstallations getInstallations(Map<String, Object> arguments) {
return FirebaseInstallations.getInstance(app);
}

private FirebaseInstallations getInstallations(AppInstallationsPigeonFirebaseApp appArg) {
@NonNull String appName = appArg.getAppName();
FirebaseApp app = FirebaseApp.getInstance(appName);
return FirebaseInstallations.getInstance(app);
}

private Task<String> getId(Map<String, Object> arguments) {
TaskCompletionSource<String> taskCompletionSource = new TaskCompletionSource<>();

Expand Down Expand Up @@ -145,6 +160,85 @@ private Task<Void> deleteId(Map<String, Object> arguments) {
return taskCompletionSource.getTask();
}

// Pigeon FirebaseAppInstallationsHostApi implementation.

@Override
public void initializeApp(
@NonNull AppInstallationsPigeonFirebaseApp app,
@NonNull AppInstallationsPigeonSettings settings,
@NonNull GeneratedAndroidFirebaseAppInstallations.VoidResult result) {
// Currently there is no per-app configurable behavior required on Android for these settings.
// We execute asynchronously to keep the threading model consistent.
cachedThreadPool.execute(
() -> {
try {
// Touch the instance to ensure it's initialized.
getInstallations(app);
result.success();
} catch (Exception e) {
result.error(e);
}
});
}

@Override
public void delete(
@NonNull AppInstallationsPigeonFirebaseApp app,
@NonNull GeneratedAndroidFirebaseAppInstallations.VoidResult result) {
cachedThreadPool.execute(
() -> {
try {
Tasks.await(getInstallations(app).delete());
result.success();
} catch (Exception e) {
result.error(e);
}
});
}

@Override
public void getId(
@NonNull AppInstallationsPigeonFirebaseApp app,
@NonNull GeneratedAndroidFirebaseAppInstallations.Result<String> result) {
cachedThreadPool.execute(
() -> {
try {
String id = Tasks.await(getInstallations(app).getId());
result.success(id);
} catch (Exception e) {
result.error(e);
}
});
}

@Override
public void getToken(
@NonNull AppInstallationsPigeonFirebaseApp app,
@NonNull Boolean forceRefresh,
@NonNull GeneratedAndroidFirebaseAppInstallations.Result<String> result) {
cachedThreadPool.execute(
() -> {
try {
FirebaseInstallations firebaseInstallations = getInstallations(app);
InstallationTokenResult tokenResult =
Tasks.await(firebaseInstallations.getToken(forceRefresh));
result.success(tokenResult.getToken());
} catch (Exception e) {
result.error(e);
}
});
}

@Override
public void onIdChange(
@NonNull AppInstallationsPigeonFirebaseApp app,
@NonNull String newId,
@NonNull GeneratedAndroidFirebaseAppInstallations.VoidResult result) {
// The Dart side currently uses an EventChannel-based listener, so this Pigeon hook
// is a no-op placeholder to satisfy the interface.
result.success();
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
Task<?> methodCallTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import FirebaseInstallations

let kFLTFirebaseInstallationsChannelName = "plugins.flutter.io/firebase_app_installations"

public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin {
public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol,
FlutterPlugin, FirebaseAppInstallationsHostApi {
private var eventSink: FlutterEventSink?
private var messenger: FlutterBinaryMessenger
private var streamHandler = [String: IdChangedStreamHandler?]()
Expand All @@ -42,6 +43,9 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F
let instance = FirebaseInstallationsPlugin(messenger: binaryMessenger)
FLTFirebasePluginRegistry.sharedInstance().register(instance)
registrar.addMethodCallDelegate(instance, channel: channel)

// Set up Pigeon host API handlers for Dart-side FirebaseAppInstallationsHostApi.
SetUpFirebaseAppInstallationsHostApi(binaryMessenger, instance)
}

public func firebaseLibraryVersion() -> String {
Expand Down Expand Up @@ -71,6 +75,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F
return Installations.installations(app: app)
}

private func getInstallations(app: AppInstallationsPigeonFirebaseApp) -> Installations {
getInstallations(appName: app.appName)
}

/// Gets Installations Id for an instance.
/// - Parameter arguments: the arguments passed by the Dart calling method
/// - Parameter result: the result instance used to send the result to Dart.
Expand Down Expand Up @@ -122,6 +130,80 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F
}
}

// MARK: - FirebaseAppInstallationsHostApi (Pigeon)

public func initializeAppApp(
_ app: AppInstallationsPigeonFirebaseApp,
settings: AppInstallationsPigeonSettings,
completion: @escaping (FlutterError?) -> Void
) {
// Currently no per-app settings are applied on iOS; ensure the instance is created.
_ = getInstallations(app: app)
completion(nil)
}

public func deleteApp(
_ app: AppInstallationsPigeonFirebaseApp,
completion: @escaping (FlutterError?) -> Void
) {
let instance = getInstallations(app: app)
instance.delete { error in
if let error = error as NSError? {
let code = self
.mapInstallationsErrorCodes(code: UInt(error.code)) as String
completion(FlutterError(code: code, message: error.localizedDescription, details: nil))
} else {
completion(nil)
}
}
}

public func getIdApp(
_ app: AppInstallationsPigeonFirebaseApp,
completion: @escaping (String?, FlutterError?) -> Void
) {
let instance = getInstallations(app: app)
instance.installationID { id, error in
if let error = error as NSError? {
let code = self
.mapInstallationsErrorCodes(code: UInt(error.code)) as String
completion(nil, FlutterError(code: code, message: error.localizedDescription, details: nil))
} else {
completion(id, nil)
}
}
}

public func getTokenApp(
_ app: AppInstallationsPigeonFirebaseApp,
forceRefresh: Bool,
completion: @escaping (String?, FlutterError?) -> Void
) {
let instance = getInstallations(app: app)
instance.authTokenForcingRefresh(forceRefresh) { tokenResult, error in
if let error = error as NSError? {
let code = self
.mapInstallationsErrorCodes(code: UInt(error.code)) as String
completion(
nil,
FlutterError(code: code, message: error.localizedDescription, details: nil)
)
} else {
completion(tokenResult?.authToken, nil)
}
}
}

public func onIdChangeApp(
_ app: AppInstallationsPigeonFirebaseApp,
newId: String,
completion: @escaping (FlutterError?) -> Void
) {
// The Dart side currently uses an EventChannel-based listener, so this Pigeon hook
// is a no-op placeholder to satisfy the interface.
completion(nil)
}

/// Registers a listener for changes in the Installations Id.
/// - Parameter arguments: the arguments passed by the Dart calling method
/// - Parameter result: the result instance used to send the result to Dart.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2025, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
// See also: https://pub.dev/packages/pigeon

#import <Foundation/Foundation.h>

@protocol FlutterBinaryMessenger;
@protocol FlutterMessageCodec;
@class FlutterError;
@class FlutterStandardTypedData;

NS_ASSUME_NONNULL_BEGIN

@class AppInstallationsPigeonSettings;
@class AppInstallationsPigeonFirebaseApp;

@interface AppInstallationsPigeonSettings : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithPersistenceEnabled:(BOOL )persistenceEnabled
forceRefreshOnSignIn:(BOOL )forceRefreshOnSignIn
forceRefreshOnTokenChange:(BOOL )forceRefreshOnTokenChange
forceRefreshOnAppUpdate:(BOOL )forceRefreshOnAppUpdate;
@property(nonatomic, assign) BOOL persistenceEnabled;
@property(nonatomic, assign) BOOL forceRefreshOnSignIn;
@property(nonatomic, assign) BOOL forceRefreshOnTokenChange;
@property(nonatomic, assign) BOOL forceRefreshOnAppUpdate;
@end

@interface AppInstallationsPigeonFirebaseApp : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)makeWithAppName:(NSString *)appName;
@property(nonatomic, copy) NSString * appName;
@end

/// The codec used by all APIs.
NSObject<FlutterMessageCodec> *nullGetFirebaseAppInstallationsMessagesCodec(void);

@protocol FirebaseAppInstallationsHostApi
- (void)initializeAppApp:(AppInstallationsPigeonFirebaseApp *)app settings:(AppInstallationsPigeonSettings *)settings completion:(void (^)(FlutterError *_Nullable))completion;
- (void)deleteApp:(AppInstallationsPigeonFirebaseApp *)app completion:(void (^)(FlutterError *_Nullable))completion;
- (void)getIdApp:(AppInstallationsPigeonFirebaseApp *)app completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
- (void)getTokenApp:(AppInstallationsPigeonFirebaseApp *)app forceRefresh:(BOOL)forceRefresh completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
- (void)onIdChangeApp:(AppInstallationsPigeonFirebaseApp *)app newId:(NSString *)newId completion:(void (^)(FlutterError *_Nullable))completion;
@end

extern void SetUpFirebaseAppInstallationsHostApi(id<FlutterBinaryMessenger> binaryMessenger, NSObject<FirebaseAppInstallationsHostApi> *_Nullable api);

extern void SetUpFirebaseAppInstallationsHostApiWithSuffix(id<FlutterBinaryMessenger> binaryMessenger, NSObject<FirebaseAppInstallationsHostApi> *_Nullable api, NSString *messageChannelSuffix);


@interface FirebaseAppInstallationsFlutterApi : NSObject
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger messageChannelSuffix:(nullable NSString *)messageChannelSuffix;
- (void)registerIdTokenListenerApp:(AppInstallationsPigeonFirebaseApp *)app completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
@end

NS_ASSUME_NONNULL_END
Loading
Loading