From f8a08e5292b6bc663a604a678e4a27626bc061f2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:57:22 +0000 Subject: [PATCH 1/8] Implement setDefaultEventParameters method in C++ Analytics API This commit adds the method to the C++ Analytics API. This method allows setting default event parameters, mirroring functionality in the iOS and Android SDKs. The implementation covers: - iOS: Using Objective-C++ to call the - Android: Using JNI to call the - Desktop: A stub implementation for other platforms. Unit tests and integration tests were added to ensure the change works as intended. A release note was added for the next release (v12.7.0) in - Analytics: Added support for default parameters to be sent with each event. This change lets you use to set default parameters to be sent with each event. - General (iOS): Update to Firebase Cocoapods version 11.9.0. - General (Android): Update to Firebase Android BoM version 33.10.0. All the references to in the test files were corrected to have the namespace added. --- .../integration_test/src/integration_test.cc | 93 +++++++++++-------- analytics/src/analytics_android.cc | 60 ++++++++++++ analytics/src/analytics_ios.mm | 17 ++++ analytics/src/analytics_stub.cc | 5 + analytics/src/include/firebase/analytics.h | 5 + analytics/tests/analytics_test.cc | 18 ++++ release_build_files/readme.md | 8 ++ 7 files changed, 165 insertions(+), 41 deletions(-) diff --git a/analytics/integration_test/src/integration_test.cc b/analytics/integration_test/src/integration_test.cc index bedccee516..330115d522 100644 --- a/analytics/integration_test/src/integration_test.cc +++ b/analytics/integration_test/src/integration_test.cc @@ -298,47 +298,58 @@ TEST_F(FirebaseAnalyticsTest, TestLogEventWithComplexParameters) { } TEST_F(FirebaseAnalyticsTest, TestSetConsent) { - // On Android, this test must be performed at the end, after all the tests for - // session ID and instance ID. This is because once you call SetConsent to - // deny consent on Android, calling it again to grant consent may not take - // effect until the app restarts, thus breaking any of those tests that are - // run after this one. - // - // If this test does happen to run earlier (due to randomizing test order, for - // example), the tests that could fail will be skipped (on Android). - - // Can't confirm that these do anything but just run them all to ensure the - // app doesn't crash. - std::map - consent_settings_allow = { - {firebase::analytics::kConsentTypeAnalyticsStorage, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdStorage, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdUserData, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdPersonalization, - firebase::analytics::kConsentStatusGranted}}; - std::map - consent_settings_deny = { - {firebase::analytics::kConsentTypeAnalyticsStorage, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdStorage, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdUserData, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdPersonalization, - firebase::analytics::kConsentStatusDenied}}; - std::map - consent_settings_empty; - firebase::analytics::SetConsent(consent_settings_empty); - ProcessEvents(1000); - firebase::analytics::SetConsent(consent_settings_deny); - ProcessEvents(1000); - firebase::analytics::SetConsent(consent_settings_allow); - ProcessEvents(1000); - - did_test_setconsent_ = true; + // On Android, this test must be performed at the end, after all the tests for + // session ID and instance ID. This is because once you call SetConsent to + // deny consent on Android, calling it again to grant consent may not take + // effect until the app restarts, thus breaking any of those tests that are + // run after this one. + // + // If this test does happen to run earlier (due to randomizing test order, for + // example), the tests that could fail will be skipped (on Android). + + // Can't confirm that these do anything but just run them all to ensure the + // app doesn't crash. + std::map + consent_settings_allow = { + {firebase::analytics::kConsentTypeAnalyticsStorage, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdStorage, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdUserData, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdPersonalization, + firebase::analytics::kConsentStatusGranted}}; + std::map + consent_settings_deny = { + {firebase::analytics::kConsentTypeAnalyticsStorage, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdStorage, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdUserData, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdPersonalization, + firebase::analytics::kConsentStatusDenied}}; + std::map + consent_settings_empty; + firebase::analytics::SetConsent(consent_settings_empty); + ProcessEvents(1000); + firebase::analytics::SetConsent(consent_settings_deny); + ProcessEvents(1000); + firebase::analytics::SetConsent(consent_settings_allow); + ProcessEvents(1000); + + did_test_setconsent_ = true; } +TEST_F(FirebaseAnalyticsTest, TestSetDefaultEventParameters) { + std::map default_params = { + {"key1", firebase::Variant("value1")}, + {"key2", firebase::Variant(12345)}, + {"key3", firebase::Variant(1.01)}, + {"key4", firebase::Variant("my_value")}, + {"key5", firebase::Variant(true)}, + {"key6", firebase::Variant::EmptyMap()}, + }; + firebase::analytics::SetDefaultEventParameters(default_params); +} } // namespace firebase_testapp_automated diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index d1279c6ba7..62ba39e029 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -736,5 +736,65 @@ Future GetSessionIdLastResult() { internal::kAnalyticsFnGetSessionId)); } +// Sets the default parameters to be sent with each event. +void SetDefaultEventParameters(const std::map& parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + JNIEnv* env = g_app->GetJNIEnv(); + + jobject map = + env->NewObject(util::hash_map::GetClass(), + util::hash_map::GetMethodId(util::hash_map::kConstructor)); + util::CheckAndClearJniExceptions(env); + + jmethodID put_method_id = util::map::GetMethodId(util::map::kPut); + + for (const auto& pair : parameters) { + jstring key_string = env->NewStringUTF(pair.first.c_str()); + jobject jni_value; + if (pair.second.is_int64()) { + jni_value = env->NewObject(util::integer_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + } + else if (pair.second.is_double()){ + jni_value = env->NewObject(util::double_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + }else if (pair.second.is_string()) { + jni_value = env->NewObject(util::string_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + } + else if(pair.second.is_map()){ + jobject jni_bundle = MapToBundle(env,pair.second.map()); + jobject previous_value = env->CallObjectMethod( + bundle, put_method_id, env->NewStringUTF(key_string.c_str()), jni_bundle); + util::CheckAndClearJniExceptions(env); + if (previous_value) { + env->DeleteLocalRef(previous_value); + } + + }else { + // A Variant type that couldn't be handled was passed in. + LogError( + "LogEvent(%s): %s is not a valid parameter value type. " + "No event was logged.", + pair.first.c_str(), Variant::TypeName(pair.second.type())); + continue; + } + jobject previous_value = env->CallObjectMethod( + map, put_method_id, key_string, jni_value); + util::CheckAndClearJniExceptions(env); + env->DeleteLocalRef(jni_value); + env->DeleteLocalRef(key_string); + + } + + env->CallVoidMethod(g_analytics_class_instance, + analytics::GetMethodId(analytics::kSetDefaultEventParameters), + map); + + util::CheckAndClearJniExceptions(env); + env->DeleteLocalRef(map); + +} + } // namespace analytics } // namespace firebase diff --git a/analytics/src/analytics_ios.mm b/analytics/src/analytics_ios.mm index d56fd61356..a66764223b 100644 --- a/analytics/src/analytics_ios.mm +++ b/analytics/src/analytics_ios.mm @@ -438,5 +438,22 @@ Thread get_id_thread( internal::FutureData::Get()->api()->LastResult(internal::kAnalyticsFnGetSessionId)); } +/// @brief Sets the default parameters to be sent with each event. +/// +/// @param[in] parameters The parameters to send with each event. +void SetDefaultEventParameters(const std::map& parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + NSMutableDictionary* parameters_dict = + [[NSMutableDictionary alloc] initWithCapacity:parameters.size()]; + for (const auto& pair : parameters) { + NSString* key = SafeString(pair.first.c_str()); + if (!AddVariantToDictionary(parameters_dict, key, pair.second)) { + LogError("SetDefaultEventParameters: Unsupported type (%s) within map with key %s.", + Variant::TypeName(pair.second.type()), key); + } + } + [FIRAnalytics setDefaultEventParameters:parameters_dict]; +} + } // namespace analytics } // namespace firebase diff --git a/analytics/src/analytics_stub.cc b/analytics/src/analytics_stub.cc index 37c4b3520b..cbb670b4f9 100644 --- a/analytics/src/analytics_stub.cc +++ b/analytics/src/analytics_stub.cc @@ -186,5 +186,10 @@ Future GetSessionIdLastResult() { internal::kAnalyticsFnGetSessionId)); } +// Sets the default parameters to be sent with each event. +void SetDefaultEventParameters(const std::map& parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); +} + } // namespace analytics } // namespace firebase diff --git a/analytics/src/include/firebase/analytics.h b/analytics/src/include/firebase/analytics.h index 746df99894..dd1da71919 100644 --- a/analytics/src/include/firebase/analytics.h +++ b/analytics/src/include/firebase/analytics.h @@ -591,6 +591,11 @@ Future GetSessionId(); /// app session. Future GetSessionIdLastResult(); +/// @brief Sets the default parameters to be sent with each event. +/// +/// @param[in] parameters The parameters to send with each event. +void SetDefaultEventParameters(const std::map& parameters); + } // namespace analytics } // namespace firebase diff --git a/analytics/tests/analytics_test.cc b/analytics/tests/analytics_test.cc index 8235c69e25..db658aa30a 100644 --- a/analytics/tests/analytics_test.cc +++ b/analytics/tests/analytics_test.cc @@ -296,5 +296,23 @@ TEST_F(AnalyticsTest, TestGetAnalyticsInstanceId) { EXPECT_EQ(std::string("FakeAnalyticsInstanceId0"), *result.result()); } +TEST_F(AnalyticsTest, TestSetDefaultEventParameters) { + std::map parameters = { + {"key1", firebase::Variant("value1")}, + {"key2", firebase::Variant(12345)}, + {"key3", firebase::Variant(1.01)}, + {"key4", firebase::Variant("my_value")}, + {"key5", firebase::Variant(true)}, + {"key6", firebase::Variant::EmptyMap()}, + }; + + AddExpectationAndroid("FirebaseAnalytics.setDefaultEventParameters", + {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); + AddExpectationApple("+[FIRAnalytics setDefaultEventParameters:]", + {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); + + SetDefaultEventParameters(parameters); +} + } // namespace analytics } // namespace firebase diff --git a/release_build_files/readme.md b/release_build_files/readme.md index c659c7f452..d69bd71959 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -631,6 +631,14 @@ workflow use only during the development of your app, not for publicly shipping code. ## Release Notes +### Next Release (12.7.0) +- Changes + - Analytics: Added support for default parameters to be sent with each event. + This change lets you use `SetDefaultEventParameters` to set default + parameters to be sent with each event. + - General (iOS): Update to Firebase Cocoapods version 11.9.0. + - General (Android): Update to Firebase Android BoM version 33.10.0. + ### 12.6.0 - Changes - General (iOS): Update to Firebase Cocoapods version 11.8.1. From 5dbbfda62d5bb7e5c268639b15d8238cb53a09f8 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 14:06:48 -0800 Subject: [PATCH 2/8] Format code and update readme. --- .../integration_test/src/integration_test.cc | 86 +++++++-------- analytics/src/analytics_android.cc | 103 +++++++++--------- analytics/src/analytics_ios.mm | 14 +-- analytics/src/analytics_stub.cc | 3 +- analytics/src/include/firebase/analytics.h | 3 +- analytics/tests/analytics_test.cc | 10 +- release_build_files/readme.md | 7 +- 7 files changed, 115 insertions(+), 111 deletions(-) diff --git a/analytics/integration_test/src/integration_test.cc b/analytics/integration_test/src/integration_test.cc index 330115d522..7b398cf43e 100644 --- a/analytics/integration_test/src/integration_test.cc +++ b/analytics/integration_test/src/integration_test.cc @@ -298,51 +298,51 @@ TEST_F(FirebaseAnalyticsTest, TestLogEventWithComplexParameters) { } TEST_F(FirebaseAnalyticsTest, TestSetConsent) { - // On Android, this test must be performed at the end, after all the tests for - // session ID and instance ID. This is because once you call SetConsent to - // deny consent on Android, calling it again to grant consent may not take - // effect until the app restarts, thus breaking any of those tests that are - // run after this one. - // - // If this test does happen to run earlier (due to randomizing test order, for - // example), the tests that could fail will be skipped (on Android). - - // Can't confirm that these do anything but just run them all to ensure the - // app doesn't crash. - std::map - consent_settings_allow = { - {firebase::analytics::kConsentTypeAnalyticsStorage, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdStorage, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdUserData, - firebase::analytics::kConsentStatusGranted}, - {firebase::analytics::kConsentTypeAdPersonalization, - firebase::analytics::kConsentStatusGranted}}; - std::map - consent_settings_deny = { - {firebase::analytics::kConsentTypeAnalyticsStorage, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdStorage, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdUserData, - firebase::analytics::kConsentStatusDenied}, - {firebase::analytics::kConsentTypeAdPersonalization, - firebase::analytics::kConsentStatusDenied}}; - std::map - consent_settings_empty; - firebase::analytics::SetConsent(consent_settings_empty); - ProcessEvents(1000); - firebase::analytics::SetConsent(consent_settings_deny); - ProcessEvents(1000); - firebase::analytics::SetConsent(consent_settings_allow); - ProcessEvents(1000); - - did_test_setconsent_ = true; + // On Android, this test must be performed at the end, after all the tests for + // session ID and instance ID. This is because once you call SetConsent to + // deny consent on Android, calling it again to grant consent may not take + // effect until the app restarts, thus breaking any of those tests that are + // run after this one. + // + // If this test does happen to run earlier (due to randomizing test order, for + // example), the tests that could fail will be skipped (on Android). + + // Can't confirm that these do anything but just run them all to ensure the + // app doesn't crash. + std::map + consent_settings_allow = { + {firebase::analytics::kConsentTypeAnalyticsStorage, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdStorage, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdUserData, + firebase::analytics::kConsentStatusGranted}, + {firebase::analytics::kConsentTypeAdPersonalization, + firebase::analytics::kConsentStatusGranted}}; + std::map + consent_settings_deny = { + {firebase::analytics::kConsentTypeAnalyticsStorage, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdStorage, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdUserData, + firebase::analytics::kConsentStatusDenied}, + {firebase::analytics::kConsentTypeAdPersonalization, + firebase::analytics::kConsentStatusDenied}}; + std::map + consent_settings_empty; + firebase::analytics::SetConsent(consent_settings_empty); + ProcessEvents(1000); + firebase::analytics::SetConsent(consent_settings_deny); + ProcessEvents(1000); + firebase::analytics::SetConsent(consent_settings_allow); + ProcessEvents(1000); + + did_test_setconsent_ = true; } TEST_F(FirebaseAnalyticsTest, TestSetDefaultEventParameters) { - std::map default_params = { + std::map default_params = { {"key1", firebase::Variant("value1")}, {"key2", firebase::Variant(12345)}, {"key3", firebase::Variant(1.01)}, @@ -350,6 +350,6 @@ TEST_F(FirebaseAnalyticsTest, TestSetDefaultEventParameters) { {"key5", firebase::Variant(true)}, {"key6", firebase::Variant::EmptyMap()}, }; - firebase::analytics::SetDefaultEventParameters(default_params); + firebase::analytics::SetDefaultEventParameters(default_params); } } // namespace firebase_testapp_automated diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index 62ba39e029..b379ae9761 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -737,63 +737,64 @@ Future GetSessionIdLastResult() { } // Sets the default parameters to be sent with each event. -void SetDefaultEventParameters(const std::map& parameters) { - FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); - JNIEnv* env = g_app->GetJNIEnv(); - - jobject map = - env->NewObject(util::hash_map::GetClass(), - util::hash_map::GetMethodId(util::hash_map::kConstructor)); - util::CheckAndClearJniExceptions(env); - - jmethodID put_method_id = util::map::GetMethodId(util::map::kPut); +void SetDefaultEventParameters( + const std::map& parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + JNIEnv* env = g_app->GetJNIEnv(); - for (const auto& pair : parameters) { - jstring key_string = env->NewStringUTF(pair.first.c_str()); - jobject jni_value; - if (pair.second.is_int64()) { - jni_value = env->NewObject(util::integer_java_class_type::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); - } - else if (pair.second.is_double()){ - jni_value = env->NewObject(util::double_java_class_type::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); - }else if (pair.second.is_string()) { - jni_value = env->NewObject(util::string_java_class_type::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); - } - else if(pair.second.is_map()){ - jobject jni_bundle = MapToBundle(env,pair.second.map()); - jobject previous_value = env->CallObjectMethod( - bundle, put_method_id, env->NewStringUTF(key_string.c_str()), jni_bundle); - util::CheckAndClearJniExceptions(env); - if (previous_value) { - env->DeleteLocalRef(previous_value); - } + jobject map = + env->NewObject(util::hash_map::GetClass(), + util::hash_map::GetMethodId(util::hash_map::kConstructor)); + util::CheckAndClearJniExceptions(env); - }else { - // A Variant type that couldn't be handled was passed in. - LogError( - "LogEvent(%s): %s is not a valid parameter value type. " - "No event was logged.", - pair.first.c_str(), Variant::TypeName(pair.second.type())); - continue; - } - jobject previous_value = env->CallObjectMethod( - map, put_method_id, key_string, jni_value); - util::CheckAndClearJniExceptions(env); - env->DeleteLocalRef(jni_value); - env->DeleteLocalRef(key_string); + jmethodID put_method_id = util::map::GetMethodId(util::map::kPut); + + for (const auto& pair : parameters) { + jstring key_string = env->NewStringUTF(pair.first.c_str()); + jobject jni_value; + if (pair.second.is_int64()) { + jni_value = + env->NewObject(util::integer_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + } else if (pair.second.is_double()) { + jni_value = + env->NewObject(util::double_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + } else if (pair.second.is_string()) { + jni_value = + env->NewObject(util::string_java_class_type::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + } else if (pair.second.is_map()) { + jobject jni_bundle = MapToBundle(env, pair.second.map()); + jobject previous_value = env->CallObjectMethod( + bundle, put_method_id, env->NewStringUTF(key_string.c_str()), + jni_bundle); + util::CheckAndClearJniExceptions(env); + if (previous_value) { + env->DeleteLocalRef(previous_value); + } + } else { + // A Variant type that couldn't be handled was passed in. + LogError( + "LogEvent(%s): %s is not a valid parameter value type. " + "No event was logged.", + pair.first.c_str(), Variant::TypeName(pair.second.type())); + continue; } - - env->CallVoidMethod(g_analytics_class_instance, - analytics::GetMethodId(analytics::kSetDefaultEventParameters), - map); - + jobject previous_value = + env->CallObjectMethod(map, put_method_id, key_string, jni_value); util::CheckAndClearJniExceptions(env); - env->DeleteLocalRef(map); + env->DeleteLocalRef(jni_value); + env->DeleteLocalRef(key_string); + } + env->CallVoidMethod( + g_analytics_class_instance, + analytics::GetMethodId(analytics::kSetDefaultEventParameters), map); + + util::CheckAndClearJniExceptions(env); + env->DeleteLocalRef(map); } } // namespace analytics diff --git a/analytics/src/analytics_ios.mm b/analytics/src/analytics_ios.mm index a66764223b..922ebbaa15 100644 --- a/analytics/src/analytics_ios.mm +++ b/analytics/src/analytics_ios.mm @@ -445,14 +445,14 @@ void SetDefaultEventParameters(const std::map& parameters) FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); NSMutableDictionary* parameters_dict = [[NSMutableDictionary alloc] initWithCapacity:parameters.size()]; - for (const auto& pair : parameters) { - NSString* key = SafeString(pair.first.c_str()); - if (!AddVariantToDictionary(parameters_dict, key, pair.second)) { - LogError("SetDefaultEventParameters: Unsupported type (%s) within map with key %s.", - Variant::TypeName(pair.second.type()), key); - } + for (const auto& pair : parameters) { + NSString* key = SafeString(pair.first.c_str()); + if (!AddVariantToDictionary(parameters_dict, key, pair.second)) { + LogError("SetDefaultEventParameters: Unsupported type (%s) within map with key %s.", + Variant::TypeName(pair.second.type()), key); } - [FIRAnalytics setDefaultEventParameters:parameters_dict]; + } + [FIRAnalytics setDefaultEventParameters:parameters_dict]; } } // namespace analytics diff --git a/analytics/src/analytics_stub.cc b/analytics/src/analytics_stub.cc index cbb670b4f9..3f51ffbdab 100644 --- a/analytics/src/analytics_stub.cc +++ b/analytics/src/analytics_stub.cc @@ -187,7 +187,8 @@ Future GetSessionIdLastResult() { } // Sets the default parameters to be sent with each event. -void SetDefaultEventParameters(const std::map& parameters) { +void SetDefaultEventParameters( + const std::map& parameters) { FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); } diff --git a/analytics/src/include/firebase/analytics.h b/analytics/src/include/firebase/analytics.h index dd1da71919..6efc60048c 100644 --- a/analytics/src/include/firebase/analytics.h +++ b/analytics/src/include/firebase/analytics.h @@ -594,7 +594,8 @@ Future GetSessionIdLastResult(); /// @brief Sets the default parameters to be sent with each event. /// /// @param[in] parameters The parameters to send with each event. -void SetDefaultEventParameters(const std::map& parameters); +void SetDefaultEventParameters( + const std::map& parameters); } // namespace analytics } // namespace firebase diff --git a/analytics/tests/analytics_test.cc b/analytics/tests/analytics_test.cc index db658aa30a..2d80b4c8f6 100644 --- a/analytics/tests/analytics_test.cc +++ b/analytics/tests/analytics_test.cc @@ -306,10 +306,12 @@ TEST_F(AnalyticsTest, TestSetDefaultEventParameters) { {"key6", firebase::Variant::EmptyMap()}, }; - AddExpectationAndroid("FirebaseAnalytics.setDefaultEventParameters", - {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); - AddExpectationApple("+[FIRAnalytics setDefaultEventParameters:]", - {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); + AddExpectationAndroid( + "FirebaseAnalytics.setDefaultEventParameters", + {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); + AddExpectationApple( + "+[FIRAnalytics setDefaultEventParameters:]", + {"key1=value1,key2=12345,key3=1.01,key4=my_value,key5=1"}); SetDefaultEventParameters(parameters); } diff --git a/release_build_files/readme.md b/release_build_files/readme.md index d69bd71959..6f026792af 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -631,13 +631,12 @@ workflow use only during the development of your app, not for publicly shipping code. ## Release Notes -### Next Release (12.7.0) +### 12.7.0 - Changes - - Analytics: Added support for default parameters to be sent with each event. - This change lets you use `SetDefaultEventParameters` to set default - parameters to be sent with each event. - General (iOS): Update to Firebase Cocoapods version 11.9.0. - General (Android): Update to Firebase Android BoM version 33.10.0. + - Analytics: Added support for setting default parameters to be sent + with each event, using `SetDefaultEventParameters`. ### 12.6.0 - Changes From fff19cac6f1894a63e4c7b820ce77db4de3a7b9f Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 15:04:33 -0800 Subject: [PATCH 3/8] Update Android implementation. --- analytics/src/analytics_android.cc | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index b379ae9761..b00518a281 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -750,30 +750,23 @@ void SetDefaultEventParameters( jmethodID put_method_id = util::map::GetMethodId(util::map::kPut); for (const auto& pair : parameters) { - jstring key_string = env->NewStringUTF(pair.first.c_str()); jobject jni_value; if (pair.second.is_int64()) { jni_value = - env->NewObject(util::integer_java_class_type::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); + env->NewObject(util::integer_class::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor), + pair.second.int64_value()); } else if (pair.second.is_double()) { jni_value = - env->NewObject(util::double_java_class_type::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); + env->NewObject(util::double_class::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor), + pair.second.double_value()); } else if (pair.second.is_string()) { jni_value = - env->NewObject(util::string_java_class_type::GetClass(), + env->NewObject(util::string::GetClass(), util::bundle::GetMethodId(util::bundle::kConstructor)); } else if (pair.second.is_map()) { - jobject jni_bundle = MapToBundle(env, pair.second.map()); - jobject previous_value = env->CallObjectMethod( - bundle, put_method_id, env->NewStringUTF(key_string.c_str()), - jni_bundle); - util::CheckAndClearJniExceptions(env); - if (previous_value) { - env->DeleteLocalRef(previous_value); - } - + jni_value = env->NewStringUTF(pair.second.c_str()); } else { // A Variant type that couldn't be handled was passed in. LogError( @@ -782,11 +775,13 @@ void SetDefaultEventParameters( pair.first.c_str(), Variant::TypeName(pair.second.type())); continue; } + jstring key_string = env->NewStringUTF(pair.first.c_str()); jobject previous_value = env->CallObjectMethod(map, put_method_id, key_string, jni_value); util::CheckAndClearJniExceptions(env); env->DeleteLocalRef(jni_value); env->DeleteLocalRef(key_string); + env->DeleteLocalRef(previous_value); } env->CallVoidMethod( From 8d6d7d63e7924990180b60d462c701c843328931 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 15:23:17 -0800 Subject: [PATCH 4/8] Update JNI usage and add method definition. --- analytics/src/analytics_android.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index b00518a281..b91218e914 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -58,6 +58,8 @@ static const ::firebase::App* g_app = nullptr; "()Lcom/google/android/gms/tasks/Task;"), \ X(GetSessionId, "getSessionId", \ "()Lcom/google/android/gms/tasks/Task;"), \ + X(SetDefaultEventParameters, "setDefaultEventParameters", \ + "(Landroid/os/Bundle;)V"), \ X(GetInstance, "getInstance", "(Landroid/content/Context;)" \ "Lcom/google/firebase/analytics/FirebaseAnalytics;", \ firebase::util::kMethodTypeStatic) @@ -755,27 +757,27 @@ void SetDefaultEventParameters( jni_value = env->NewObject(util::integer_class::GetClass(), util::bundle::GetMethodId(util::bundle::kConstructor), - pair.second.int64_value()); + pair.second.int64_value()); } else if (pair.second.is_double()) { jni_value = env->NewObject(util::double_class::GetClass(), util::bundle::GetMethodId(util::bundle::kConstructor), - pair.second.double_value()); + pair.second.double_value()); } else if (pair.second.is_string()) { jni_value = env->NewObject(util::string::GetClass(), util::bundle::GetMethodId(util::bundle::kConstructor)); } else if (pair.second.is_map()) { - jni_value = env->NewStringUTF(pair.second.c_str()); + jni_value = env->NewStringUTF(pair.second.string_value()); } else { // A Variant type that couldn't be handled was passed in. LogError( "LogEvent(%s): %s is not a valid parameter value type. " "No event was logged.", - pair.first.c_str(), Variant::TypeName(pair.second.type())); + pair.first.string_value(), Variant::TypeName(pair.second.type())); continue; } - jstring key_string = env->NewStringUTF(pair.first.c_str()); + jstring key_string = env->NewStringUTF(pair.first.string_value()); jobject previous_value = env->CallObjectMethod(map, put_method_id, key_string, jni_value); util::CheckAndClearJniExceptions(env); From d6ca0478cb12b49bff2090a8f37e3f66984781b5 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 15:31:07 -0800 Subject: [PATCH 5/8] Fix map access to use strings. --- analytics/src/analytics_android.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index b91218e914..68800e249d 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -774,10 +774,10 @@ void SetDefaultEventParameters( LogError( "LogEvent(%s): %s is not a valid parameter value type. " "No event was logged.", - pair.first.string_value(), Variant::TypeName(pair.second.type())); + pair.first.c_str(), Variant::TypeName(pair.second.type())); continue; } - jstring key_string = env->NewStringUTF(pair.first.string_value()); + jstring key_string = env->NewStringUTF(pair.first.c_str()); jobject previous_value = env->CallObjectMethod(map, put_method_id, key_string, jni_value); util::CheckAndClearJniExceptions(env); From 3cc444719b75d9aadb06e82c1e6622204462adef Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 15:40:04 -0800 Subject: [PATCH 6/8] Add write permission for integration test, so it can do labeling. --- .github/workflows/integration_tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 634a93f6df..bbb5b5d309 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -42,6 +42,8 @@ on: firestore_dep_source: description: 'Optional: Where to get firestore iOS SDK from: "RELEASED", "TIP" or " from firestore-ios-sdk"' +permissions: write-all + env: triggerLabelPrefix: "tests-requested: " triggerLabelFull: "tests-requested: full" From 908f624a3f4d698c0ffcb5988e7bf213a326f75e Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 15:57:54 -0800 Subject: [PATCH 7/8] Fix usage of MapToBundle. --- analytics/src/analytics_android.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index 68800e249d..082b902aba 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -756,19 +756,17 @@ void SetDefaultEventParameters( if (pair.second.is_int64()) { jni_value = env->NewObject(util::integer_class::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor), + util::integer_class::GetMethodId(util::integer_class::kConstructor), pair.second.int64_value()); } else if (pair.second.is_double()) { jni_value = env->NewObject(util::double_class::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor), + util::double_class::GetMethodId(util::double_class::kConstructor), pair.second.double_value()); } else if (pair.second.is_string()) { - jni_value = - env->NewObject(util::string::GetClass(), - util::bundle::GetMethodId(util::bundle::kConstructor)); - } else if (pair.second.is_map()) { jni_value = env->NewStringUTF(pair.second.string_value()); + } else if (pair.second.is_map()) { + jni_value = MapToBundle(env, pair.second.map()); } else { // A Variant type that couldn't be handled was passed in. LogError( From 5b5571162d6f5884327e3cb31ae3521ee26130b3 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 5 Mar 2025 16:06:24 -0800 Subject: [PATCH 8/8] Format code. --- analytics/src/analytics_android.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index 082b902aba..af4610dd77 100644 --- a/analytics/src/analytics_android.cc +++ b/analytics/src/analytics_android.cc @@ -754,15 +754,15 @@ void SetDefaultEventParameters( for (const auto& pair : parameters) { jobject jni_value; if (pair.second.is_int64()) { - jni_value = - env->NewObject(util::integer_class::GetClass(), - util::integer_class::GetMethodId(util::integer_class::kConstructor), - pair.second.int64_value()); + jni_value = env->NewObject( + util::integer_class::GetClass(), + util::integer_class::GetMethodId(util::integer_class::kConstructor), + pair.second.int64_value()); } else if (pair.second.is_double()) { - jni_value = - env->NewObject(util::double_class::GetClass(), - util::double_class::GetMethodId(util::double_class::kConstructor), - pair.second.double_value()); + jni_value = env->NewObject( + util::double_class::GetClass(), + util::double_class::GetMethodId(util::double_class::kConstructor), + pair.second.double_value()); } else if (pair.second.is_string()) { jni_value = env->NewStringUTF(pair.second.string_value()); } else if (pair.second.is_map()) {