Skip to content
Merged
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
31 changes: 31 additions & 0 deletions packages/firebase_ai/firebase_ai/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ buildscript {
}
}

repositories {
google()
mavenCentral()
}

def firebaseCoreProject = findProject(':firebase_core')
if (firebaseCoreProject == null) {
throw new GradleException('Could not find the firebase_core FlutterFire plugin, have you added it as a dependency in your pubspec?')
} else if (!firebaseCoreProject.properties['FirebaseSDKVersion']) {
throw new GradleException('A newer version of the firebase_core FlutterFire plugin is required, please update your firebase_core pubspec dependency.')
}

def getRootProjectExtOrCoreProperty(name, firebaseCoreProject) {
if (!rootProject.ext.has('FlutterFire')) return firebaseCoreProject.properties[name]
if (!rootProject.ext.get('FlutterFire')[name]) return firebaseCoreProject.properties[name]
return rootProject.ext.get('FlutterFire').get(name)
}

android {
if (project.android.hasProperty("namespace")) {
namespace 'io.flutter.plugins.firebase.ai'
Expand All @@ -41,9 +59,20 @@ android {
main.java.srcDirs += "src/main/kotlin"
}

buildFeatures {
buildConfig = true
}

lintOptions {
disable 'InvalidPackage'
}

dependencies {
api firebaseCoreProject
implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}")
implementation 'com.google.firebase:firebase-common'
implementation 'androidx.annotation:annotation:1.7.0'
}
}

plugins.withId("org.jetbrains.kotlin.android") {
Expand All @@ -53,3 +82,5 @@ plugins.withId("org.jetbrains.kotlin.android") {
}
}
}

apply from: file("./user-agent.gradle")
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.flutter.plugins.firebase.ai">
<application>
<service android:name="com.google.firebase.components.ComponentDiscoveryService">
<meta-data android:name="com.google.firebase.components:io.flutter.plugins.firebase.ai.FlutterFirebaseAppRegistrar"
android:value="com.google.firebase.components.ComponentRegistrar" />
</service>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package io.flutter.plugins.firebase.ai

import androidx.annotation.Keep
import com.google.firebase.components.Component
import com.google.firebase.components.ComponentRegistrar
import com.google.firebase.platforminfo.LibraryVersionComponent

@Keep
class FlutterFirebaseAppRegistrar : ComponentRegistrar {
override fun getComponents(): List<Component<*>> {
return listOf(
LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION))
}
}
22 changes: 22 additions & 0 deletions packages/firebase_ai/firebase_ai/android/user-agent.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import java.util.regex.Matcher
import java.util.regex.Pattern

String libraryVersionName = "UNKNOWN"
String libraryName = "flutter-fire-ai"
File pubspec = new File(project.projectDir.parentFile, 'pubspec.yaml')

if (pubspec.exists()) {
String yaml = pubspec.text
// Using \s*['|"]?([^\n|'|"]*)['|"]? to extract version number.
Matcher versionMatcher = Pattern.compile("^version:\\s*['|\"]?([^\\n|'|\"]*)['|\"]?\$", Pattern.MULTILINE).matcher(yaml)
if (versionMatcher.find()) libraryVersionName = versionMatcher.group(1).replaceAll("\\+", "-")
}

android {
defaultConfig {
// BuildConfig.VERSION_NAME
buildConfigField 'String', 'LIBRARY_VERSION', "\"${libraryVersionName}\""
// BuildConfig.LIBRARY_NAME
buildConfigField 'String', 'LIBRARY_NAME', "\"${libraryName}\""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Auto-generated file. Do not edit.
public let versionNumber = "5.0.0"
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ public class FirebaseAIPlugin: NSObject, FlutterPlugin {
)
let instance = FirebaseAIPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)

let selector = NSSelectorFromString("registerLibrary:withVersion:")
if let firAppClass = NSClassFromString("FIRApp") as? NSObject.Type,
firAppClass.responds(to: selector)
{
_ = firAppClass.perform(
selector,
with: "flutter-fire-ai",
with: versionNumber
)
}
Comment thread
kevmoo marked this conversation as resolved.
Resolved
}

public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
Expand Down
7 changes: 6 additions & 1 deletion packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import 'package:meta/meta.dart';

import '../firebase_ai.dart';
import 'base_model.dart';
import 'firebaseai_version.dart';
import 'platform_version_stub.dart'
if (dart.library.js_interop) 'platform_version_web.dart';

const _defaultVertexAILocation = 'us-central1';
const _defaultAgentPlatformLocation = 'global';
Expand All @@ -36,7 +39,9 @@ class FirebaseAI extends FirebasePlugin {
this.auth,
this.useLimitedUseAppCheckTokens = false,
}) : _useAgentPlatform = useAgentPlatform,
super(app.name, 'plugins.flutter.io/firebase_vertexai');
super(app.name, 'plugins.flutter.io/firebase_vertexai') {
registerWebLibraryVersion('flutter-fire-ai', packageVersion);
}

/// The [FirebaseApp] for this current [FirebaseAI] instance.
FirebaseApp app;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Stub implementation of [registerWebLibraryVersion] on non-web platforms.
void registerWebLibraryVersion(String libraryName, String version) {}
40 changes: 40 additions & 0 deletions packages/firebase_ai/firebase_ai/lib/src/platform_version_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:js_interop';
import 'dart:js_interop_unsafe';

bool _registered = false;

/// Registers [libraryName] and [version] with the Firebase Web JS SDK.
void registerWebLibraryVersion(String libraryName, String version) {
if (_registered) return;
try {
final core = globalContext.getProperty('firebase_core'.toJS) as JSObject?;
if (core != null && core.hasProperty('registerVersion'.toJS).toDart) {
final storage =
globalContext.getProperty('sessionStorage'.toJS) as JSObject?;
final key = 'flutterfire-$libraryName-$version'.toJS;
if (storage?.callMethod<JSAny?>('getItem'.toJS, key) == null) {
storage?.callMethod<JSAny?>('setItem'.toJS, key, version.toJS);
core.callMethod<JSAny?>(
'registerVersion'.toJS,
libraryName.toJS,
version.toJS,
);
}
_registered = true;
}
} catch (_) {}
}
Comment thread
kevmoo marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ class FirebaseCoreWeb extends FirebasePlatform {

static void registerLibraryVersion(String libraryName, String version) {
_libraryVersions[libraryName] = version;
if (globalContext.getProperty('firebase_core'.toJS) != null) {
_registerVersionIfNeeded(libraryName, version);
}
}

static void _registerAllLibraryVersions() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2026 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@TestOn('browser')
library;

import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:web/web.dart' as web;

String _getPlatformInfoString() {
final core = globalContext.getProperty<JSObject>('firebase_core'.toJS);
final jsApp = core.callMethod<JSObject>('getApp'.toJS);
final container = jsApp.getProperty<JSObject>('container'.toJS);
final provider = container.callMethod<JSObject>(
'getProvider'.toJS,
'platform-logger'.toJS,
);
final logger = provider.callMethod<JSObject>('getImmediate'.toJS);
return logger.callMethod<JSString>('getPlatformInfoString'.toJS).toDart;
}

void main() {
group('FirebaseCoreWeb late library version registration', () {
setUp(() {
FirebasePlatform.instance = FirebaseCoreWeb();
web.window.sessionStorage.clear();
});

test(
'registers library version with PlatformLogger when called after initializeApp',
() async {
const options = FirebaseOptions(
apiKey: 'fake-api-key',
appId: '1:1234567890:web:fake',
messagingSenderId: '1234567890',
projectId: 'fake-project',
authDomain: 'fake-project.firebaseapp.com',
);

await FirebasePlatform.instance.initializeApp(options: options);

final initialPlatformInfo = _getPlatformInfoString();
expect(initialPlatformInfo, contains('flutter-fire-core/'));
expect(
initialPlatformInfo,
isNot(contains('flutter-fire-late-test/1.2.3')),
);

FirebaseCoreWeb.registerLibraryVersion(
'flutter-fire-late-test',
'1.2.3',
);

expect(
web.window.sessionStorage
.getItem('flutterfire-flutter-fire-late-test-1.2.3'),
'1.2.3',
);
expect(
_getPlatformInfoString(),
contains('flutter-fire-late-test/1.2.3'),
);

// Clearing sessionStorage allows re-registering a subsequent version.
web.window.sessionStorage.clear();
FirebaseCoreWeb.registerLibraryVersion(
'flutter-fire-late-test-2',
'2.0.0',
);
expect(
web.window.sessionStorage
.getItem('flutterfire-flutter-fire-late-test-2-2.0.0'),
'2.0.0',
);
expect(
_getPlatformInfoString(),
contains('flutter-fire-late-test-2/2.0.0'),
);
},
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import 'package:firebase_data_connect/src/common/common_library.dart';
import 'package:firebase_data_connect/src/core/ref.dart';
import 'package:flutter/foundation.dart';

import './network/hot_restart_guard_stub.dart'
if (dart.library.js_interop) './network/hot_restart_guard_web.dart';
import './network/rest_library.dart';
import './network/transport_library.dart';

import 'cache/cache_data_types.dart';
import 'cache/cache.dart';
import 'dataconnect_version.dart';

/// DataConnect class
class FirebaseDataConnect extends FirebasePlugin {
Expand All @@ -48,6 +51,7 @@ class FirebaseDataConnect extends FirebasePlugin {
connectorConfig.serviceId,
),
super(app.name, 'plugins.flutter.io/firebase_data_connect') {
registerWebLibraryVersion('flutter-fire-dc', packageVersion);
_queryManager = QueryManager(this);
if (sdkType != null) {
_sdkType = sdkType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ String? claimDataConnectWebSocketTransport(String key) => null;
bool isCurrentDataConnectWebSocketTransport(String key, String? token) => true;

void releaseDataConnectWebSocketTransport(String key, String? token) {}

void registerWebLibraryVersion(String libraryName, String version) {}
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,26 @@ void releaseDataConnectWebSocketTransport(String key, String? token) {
globalContext.delete(key.toJS);
}
}

bool _registered = false;

void registerWebLibraryVersion(String libraryName, String version) {
if (_registered) return;
try {
final core = globalContext.getProperty('firebase_core'.toJS) as JSObject?;
if (core != null && core.hasProperty('registerVersion'.toJS).toDart) {
final storage =
globalContext.getProperty('sessionStorage'.toJS) as JSObject?;
final key = 'flutterfire-$libraryName-$version'.toJS;
if (storage?.callMethod<JSAny?>('getItem'.toJS, key) == null) {
storage?.callMethod<JSAny?>('setItem'.toJS, key, version.toJS);
core.callMethod<JSAny?>(
'registerVersion'.toJS,
libraryName.toJS,
version.toJS,
);
}
_registered = true;
}
} catch (_) {}
}
Comment thread
kevmoo marked this conversation as resolved.
Resolved
1 change: 1 addition & 0 deletions scripts/generate_versions_spm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void updateLibraryVersionPureSwiftPlugins() {
'firebase_in_app_messaging',
'firebase_crashlytics',
'firebase_core',
'firebase_ai',
];

for (final package in packages) {
Expand Down
Loading