Skip to content

Commit 7132d77

Browse files
authored
feat(firebase_analytics): retrieves appInstanceId property on native platforms if available (#8689)
* fix(example): Bump compileSdkVersion for the example Android app to fix compilation * feat(firebase_analytics): add appInstanceId property This value is needed to send server-side events through the Measurement Protocol as a part of the same session, and also useful for debugging to pair up with the data visible in DebugView and BigQuery.
1 parent f020167 commit 7132d77

File tree

15 files changed

+170
-1
lines changed

15 files changed

+170
-1
lines changed

packages/firebase_analytics/firebase_analytics/android/src/main/java/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import androidx.annotation.NonNull;
1111
import com.google.android.gms.tasks.Task;
1212
import com.google.android.gms.tasks.TaskCompletionSource;
13+
import com.google.android.gms.tasks.Tasks;
1314
import com.google.firebase.FirebaseApp;
1415
import com.google.firebase.analytics.FirebaseAnalytics;
1516
import io.flutter.embedding.engine.plugins.FlutterPlugin;
@@ -130,6 +131,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
130131
case "Analytics#setDefaultEventParameters":
131132
methodCallTask = setDefaultEventParameters(call.arguments());
132133
break;
134+
case "Analytics#getAppInstanceId":
135+
methodCallTask = handleGetAppInstanceId();
136+
break;
133137
default:
134138
result.notImplemented();
135139
return;
@@ -312,6 +316,21 @@ private Task<Void> setDefaultEventParameters(final Map<String, Object> arguments
312316
return taskCompletionSource.getTask();
313317
}
314318

319+
private Task<String> handleGetAppInstanceId() {
320+
TaskCompletionSource<String> taskCompletionSource = new TaskCompletionSource<>();
321+
322+
cachedThreadPool.execute(
323+
() -> {
324+
try {
325+
taskCompletionSource.setResult(Tasks.await(analytics.getAppInstanceId()));
326+
} catch (Exception e) {
327+
taskCompletionSource.setException(e);
328+
}
329+
});
330+
331+
return taskCompletionSource.getTask();
332+
}
333+
315334
@Override
316335
public Task<Map<String, Object>> getPluginConstantsForFirebaseApp(FirebaseApp firebaseApp) {
317336
TaskCompletionSource<Map<String, Object>> taskCompletionSource = new TaskCompletionSource<>();

packages/firebase_analytics/firebase_analytics/example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2626
apply plugin: 'com.google.gms.google-services'
2727

2828
android {
29-
compileSdkVersion 29
29+
compileSdkVersion 31
3030

3131
lintOptions {
3232
disable 'InvalidPackage'

packages/firebase_analytics/firebase_analytics/example/lib/main.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ class _MyHomePageState extends State<MyHomePage> {
113113
setMessage('setUserProperty succeeded');
114114
}
115115

116+
Future<void> _testAppInstanceId() async {
117+
String? id = await widget.analytics.appInstanceId;
118+
if (id != null) {
119+
setMessage('appInstanceId succeeded: $id');
120+
} else {
121+
setMessage('appInstanceId failed, consent declined');
122+
}
123+
}
124+
125+
Future<void> _testResetAnalyticsData() async {
126+
await widget.analytics.resetAnalyticsData();
127+
setMessage('resetAnalyticsData succeeded');
128+
}
129+
116130
AnalyticsEventItem itemCreator() {
117131
return AnalyticsEventItem(
118132
affiliation: 'affil',
@@ -303,6 +317,14 @@ class _MyHomePageState extends State<MyHomePage> {
303317
onPressed: _testSetUserProperty,
304318
child: const Text('Test setUserProperty'),
305319
),
320+
MaterialButton(
321+
onPressed: _testAppInstanceId,
322+
child: const Text('Test appInstanceId'),
323+
),
324+
MaterialButton(
325+
onPressed: _testResetAnalyticsData,
326+
child: const Text('Test resetAnalyticsData'),
327+
),
306328
Text(
307329
_message,
308330
style: const TextStyle(color: Color.fromARGB(255, 0, 155, 0)),

packages/firebase_analytics/firebase_analytics/example/test_driver/firebase_analytics_e2e.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,29 @@ void testsMain() {
195195
}
196196
},
197197
);
198+
199+
test('appInstanceId', () async {
200+
if (kIsWeb) {
201+
await expectLater(
202+
FirebaseAnalytics.instance.appInstanceId,
203+
throwsA(isA<UnimplementedError>()),
204+
);
205+
} else {
206+
final result = await FirebaseAnalytics.instance.appInstanceId;
207+
expect(result, isNull);
208+
209+
await expectLater(
210+
FirebaseAnalytics.instance.setConsent(
211+
analyticsStorageConsentGranted: true,
212+
adStorageConsentGranted: false,
213+
),
214+
completes,
215+
);
216+
217+
final result2 = await FirebaseAnalytics.instance.appInstanceId;
218+
expect(result2, isA<String>());
219+
}
220+
});
198221
});
199222
}
200223

packages/firebase_analytics/firebase_analytics/ios/Classes/FLTFirebaseAnalyticsPlugin.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
7070
[self setConsent:call.arguments withMethodCallResult:methodCallResult];
7171
} else if ([@"Analytics#setDefaultEventParameters" isEqualToString:call.method]) {
7272
[self setDefaultEventParameters:call.arguments withMethodCallResult:methodCallResult];
73+
} else if ([@"Analytics#getAppInstanceId" isEqualToString:call.method]) {
74+
[self getAppInstanceIdWithMethodCallResult:methodCallResult];
7375
} else {
7476
result(FlutterMethodNotImplemented);
7577
}
@@ -142,6 +144,11 @@ - (void)setDefaultEventParameters:(id)arguments
142144
result.success(nil);
143145
}
144146

147+
- (void)getAppInstanceIdWithMethodCallResult:(FLTFirebaseMethodCallResult *)result {
148+
NSString *appInstanceID = [FIRAnalytics appInstanceID];
149+
result.success(appInstanceID);
150+
}
151+
145152
#pragma mark - FLTFirebasePlugin
146153

147154
- (void)didReinitializeFirebaseCore:(void (^_Nonnull)(void))completion {

packages/firebase_analytics/firebase_analytics/lib/src/firebase_analytics.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ class FirebaseAnalytics extends FirebasePluginPlatform {
6969
return _delegate.isSupported();
7070
}
7171

72+
/// Retrieves the app instance id from the service, or null if consent has
73+
/// been denied.
74+
Future<String?> get appInstanceId {
75+
return _delegate.getAppInstanceId();
76+
}
77+
7278
/// Logs a custom Flutter Analytics event with the given [name] and event [parameters].
7379
Future<void> logEvent({
7480
required String name,

packages/firebase_analytics/firebase_analytics/test/firebase_analytics_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,19 @@ void main() {
11951195
],
11961196
);
11971197
});
1198+
1199+
test('appInstanceId', () async {
1200+
var _ = await analytics!.appInstanceId;
1201+
expect(
1202+
methodCallLog,
1203+
<Matcher>[
1204+
isMethodCall(
1205+
'Analytics#getAppInstanceId',
1206+
arguments: null,
1207+
)
1208+
],
1209+
);
1210+
});
11981211
});
11991212
});
12001213
}

packages/firebase_analytics/firebase_analytics/test/mock.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ void setupFirebaseAnalyticsMocks([Callback? customHandlers]) {
2020
.setMockMethodCallHandler((MethodCall methodCall) async {
2121
methodCallLog.add(methodCall);
2222
switch (methodCall.method) {
23+
case 'Analytics#getAppInstanceId':
24+
return 'ABCD1234';
25+
2326
default:
2427
return false;
2528
}

packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/method_channel/method_channel_firebase_analytics.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ class MethodChannelFirebaseAnalytics extends FirebaseAnalyticsPlatform {
168168
}
169169
}
170170

171+
@override
172+
Future<String?> getAppInstanceId() {
173+
try {
174+
return channel.invokeMethod<String?>('Analytics#getAppInstanceId');
175+
} catch (e, s) {
176+
convertPlatformException(e, s);
177+
}
178+
}
179+
171180
@override
172181
Future<void> setSessionTimeoutDuration(Duration timeout) async {
173182
try {

packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/platform_interface/platform_interface_firebase_analytics.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ abstract class FirebaseAnalyticsPlatform extends PlatformInterface {
6868
throw UnimplementedError('isSupported() is not implemented');
6969
}
7070

71+
/// Retrieves the app instance id from the service.
72+
Future<String?> getAppInstanceId() {
73+
throw UnimplementedError('getAppInstanceId() is not implemented');
74+
}
75+
7176
/// Logs the given event [name] with the given [parameters].
7277
Future<void> logEvent({
7378
required String name,

0 commit comments

Comments
 (0)