From 13aaf03dc2050514cea97023fae5c27491bcac8a Mon Sep 17 00:00:00 2001 From: Nabeel Parkar Date: Tue, 12 Sep 2023 19:19:45 +0530 Subject: [PATCH] feat(firebase_analytics): support `getSessionId` for android and apple platforms (#11478) Co-authored-by: Salakar --- .../FlutterFirebaseAnalyticsPlugin.java | 18 +++++++++++++++ .../ios/Classes/FLTFirebaseAnalyticsPlugin.m | 12 ++++++++++ .../lib/src/firebase_analytics.dart | 6 +++++ .../method_channel_firebase_analytics.dart | 9 ++++++++ ...platform_interface_firebase_analytics.dart | 4 ++++ ...ethod_channel_firebase_analytics_test.dart | 15 ++++++++++++ .../platform_interface_analytics_test.dart | 13 +++++++++++ .../lib/firebase_analytics_web.dart | 6 +++++ .../firebase_analytics_e2e_test.dart | 23 +++++++++++++++++++ 9 files changed, 106 insertions(+) diff --git a/packages/firebase_analytics/firebase_analytics/android/src/main/java/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.java b/packages/firebase_analytics/firebase_analytics/android/src/main/java/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.java index 503522f65a6f..504019deb9d0 100755 --- a/packages/firebase_analytics/firebase_analytics/android/src/main/java/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.java +++ b/packages/firebase_analytics/firebase_analytics/android/src/main/java/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.java @@ -136,6 +136,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { case "Analytics#getAppInstanceId": methodCallTask = handleGetAppInstanceId(); break; + case "Analytics#getSessionId": + methodCallTask = handleGetSessionId(); + break; default: result.notImplemented(); return; @@ -154,6 +157,21 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { }); } + private Task handleGetSessionId() { + TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); + + cachedThreadPool.execute( + () -> { + try { + taskCompletionSource.setResult(Tasks.await(analytics.getSessionId())); + } catch (Exception e) { + taskCompletionSource.setException(e); + } + }); + + return taskCompletionSource.getTask(); + } + private Task handleLogEvent(final Map arguments) { TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); diff --git a/packages/firebase_analytics/firebase_analytics/ios/Classes/FLTFirebaseAnalyticsPlugin.m b/packages/firebase_analytics/firebase_analytics/ios/Classes/FLTFirebaseAnalyticsPlugin.m index 5b38ae2d9f70..ce8da5c23f06 100644 --- a/packages/firebase_analytics/firebase_analytics/ios/Classes/FLTFirebaseAnalyticsPlugin.m +++ b/packages/firebase_analytics/firebase_analytics/ios/Classes/FLTFirebaseAnalyticsPlugin.m @@ -72,6 +72,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [self setDefaultEventParameters:call.arguments withMethodCallResult:methodCallResult]; } else if ([@"Analytics#getAppInstanceId" isEqualToString:call.method]) { [self getAppInstanceIdWithMethodCallResult:methodCallResult]; + } else if ([@"Analytics#getSessionId" isEqualToString:call.method]) { + [self getSessionIdWithMethodCallResult:methodCallResult]; } else { result(FlutterMethodNotImplemented); } @@ -79,6 +81,16 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result #pragma mark - Firebase Analytics API +- (void)getSessionIdWithMethodCallResult:(FLTFirebaseMethodCallResult *)result { + [FIRAnalytics sessionIDWithCompletion:^(int64_t sessionID, NSError *_Nullable error) { + if (error != nil) { + result.error(nil, nil, nil, error); + } else { + result.success([NSNumber numberWithLongLong:sessionID]); + } + }]; +} + - (void)logEvent:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { NSString *eventName = arguments[kFLTFirebaseAnalyticsEventName]; id parameterMap = arguments[kFLTFirebaseAnalyticsParameters]; diff --git a/packages/firebase_analytics/firebase_analytics/lib/src/firebase_analytics.dart b/packages/firebase_analytics/firebase_analytics/lib/src/firebase_analytics.dart index 18fb7ae4b489..68985331eea6 100755 --- a/packages/firebase_analytics/firebase_analytics/lib/src/firebase_analytics.dart +++ b/packages/firebase_analytics/firebase_analytics/lib/src/firebase_analytics.dart @@ -75,6 +75,12 @@ class FirebaseAnalytics extends FirebasePluginPlatform { return _delegate.getAppInstanceId(); } + /// Retrieves the session id from the client. Returns null if + /// analyticsStorageConsentGranted is false or session is expired. + Future getSessionId() { + return _delegate.getSessionId(); + } + /// Logs a custom Flutter Analytics event with the given [name] and event /// [parameters]. /// diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/method_channel/method_channel_firebase_analytics.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/method_channel/method_channel_firebase_analytics.dart index 00b42218a40a..f3617d4c8b28 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/method_channel/method_channel_firebase_analytics.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/method_channel/method_channel_firebase_analytics.dart @@ -44,6 +44,15 @@ class MethodChannelFirebaseAnalytics extends FirebaseAnalyticsPlatform { return Future.value(true); } + @override + Future getSessionId() { + try { + return channel.invokeMethod('Analytics#getSessionId'); + } catch (e, s) { + convertPlatformException(e, s); + } + } + @override Future logEvent({ required String name, diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/platform_interface/platform_interface_firebase_analytics.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/platform_interface/platform_interface_firebase_analytics.dart index 271f6e69042f..b6a7d214afb3 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/platform_interface/platform_interface_firebase_analytics.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/platform_interface/platform_interface_firebase_analytics.dart @@ -74,6 +74,10 @@ abstract class FirebaseAnalyticsPlatform extends PlatformInterface { throw UnimplementedError('getAppInstanceId() is not implemented'); } + Future getSessionId() { + throw UnimplementedError('getSessionId() is not implemented'); + } + /// Logs a custom Flutter Analytics event with the given [name] and event /// [parameters]. /// diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/test/method_channel_tests/method_channel_firebase_analytics_test.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/test/method_channel_tests/method_channel_firebase_analytics_test.dart index 04cf148f184a..db2eb9cb8bdd 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/test/method_channel_tests/method_channel_firebase_analytics_test.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/test/method_channel_tests/method_channel_firebase_analytics_test.dart @@ -24,6 +24,8 @@ void main() { switch (call.method) { case 'Analytics#getAppInstanceId': return 'ABCD1234'; + case 'Analytics#getSessionId': + return 0; default: return true; @@ -133,6 +135,19 @@ void main() { ); }); + test('getSessionId', () async { + await analytics.getSessionId(); + expect( + methodCallLogger, + [ + isMethodCall( + 'Analytics#getSessionId', + arguments: null, + ), + ], + ); + }); + test('logEvent', () async { await analytics.logEvent( name: 'test-event', diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/test/platform_interface_tests/platform_interface_analytics_test.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/test/platform_interface_tests/platform_interface_analytics_test.dart index bd27895fb2f2..bc4500788cee 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/test/platform_interface_tests/platform_interface_analytics_test.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/test/platform_interface_tests/platform_interface_analytics_test.dart @@ -211,6 +211,19 @@ void main() { ), ); }); + + test('throws if .getSessionId() not implemented', () async { + await expectLater( + () => firebaseAnalyticsPlatform.getSessionId(), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'getSessionId() is not implemented', + ), + ), + ); + }); }); } diff --git a/packages/firebase_analytics/firebase_analytics_web/lib/firebase_analytics_web.dart b/packages/firebase_analytics/firebase_analytics_web/lib/firebase_analytics_web.dart index 78bcb988a3aa..fee31d12ab37 100644 --- a/packages/firebase_analytics/firebase_analytics_web/lib/firebase_analytics_web.dart +++ b/packages/firebase_analytics/firebase_analytics_web/lib/firebase_analytics_web.dart @@ -43,6 +43,12 @@ class FirebaseAnalyticsWeb extends FirebaseAnalyticsPlatform { return analytics_interop.Analytics.isSupported(); } + @override + Future getSessionId() { + // TODO: change UnimplementedError to UnsupportedError + throw UnimplementedError('getSessionId() is not supported on Web.'); + } + @override Future logEvent({ required String name, diff --git a/tests/integration_test/firebase_analytics/firebase_analytics_e2e_test.dart b/tests/integration_test/firebase_analytics/firebase_analytics_e2e_test.dart index c40f4eb6f889..a4986d683133 100644 --- a/tests/integration_test/firebase_analytics/firebase_analytics_e2e_test.dart +++ b/tests/integration_test/firebase_analytics/firebase_analytics_e2e_test.dart @@ -19,6 +19,29 @@ void main() { ); }); + // getSessionId has to be first, else Android returns null + test( + 'getSessionId', + () async { + if (kIsWeb) { + await expectLater( + FirebaseAnalytics.instance.getSessionId(), + throwsA(isA()), + ); + } else { + await expectLater( + FirebaseAnalytics.instance.setConsent( + analyticsStorageConsentGranted: true, + ), + completes, + ); + + final result = await FirebaseAnalytics.instance.getSessionId(); + expect(result, isA()); + } + }, + ); + test('isSupported', () async { final result = await FirebaseAnalytics.instance.isSupported(); expect(result, isA());