diff --git a/analytics/integration_test/src/integration_test.cc b/analytics/integration_test/src/integration_test.cc index bedccee516..29dfe4f928 100644 --- a/analytics/integration_test/src/integration_test.cc +++ b/analytics/integration_test/src/integration_test.cc @@ -341,4 +341,70 @@ TEST_F(FirebaseAnalyticsTest, TestSetConsent) { did_test_setconsent_ = true; } +TEST_F(FirebaseAnalyticsTest, TestSetDefaultEventParameters) { + LogInfo("Testing SetDefaultEventParameters()."); + + // Set some default parameters with various types. + std::map defaults; + defaults["default_int"] = static_cast(123); // int64_t + defaults["default_double"] = 45.67; // double + defaults["default_bool"] = true; // bool + defaults["default_string"] = "test_string_value"; // const char* + defaults["default_to_clear"] = "will_be_cleared"; // Another string + + firebase::analytics::SetDefaultEventParameters(defaults); + LogInfo("Set initial default parameters."); + + // Log an event - the defaults should be included automatically by the + // underlying SDK if logging immediately after setting is supported. + // Verification might need manual checking in the Analytics console or + // via platform-specific debug logs if possible. + firebase::analytics::LogEvent("event_with_mixed_defaults"); + LogInfo("Logged event_with_mixed_defaults"); + + // Clear one default parameter and update another. + defaults["default_to_clear"] = firebase::Variant::Null(); // Clear this one + defaults["default_int"] = static_cast(999); // Update this one + firebase::analytics::SetDefaultEventParameters(defaults); + LogInfo("Cleared one parameter and updated another."); + + // Log another event. + firebase::analytics::LogEvent("event_with_one_default_cleared"); + LogInfo("Logged event_with_one_default_cleared"); + + // Set only one parameter, clearing others implicitly if underlying SDK works + // like that + std::map single_default; + single_default["only_this_double"] = 78.9; + firebase::analytics::SetDefaultEventParameters(single_default); + LogInfo("Set a single different default parameter."); + firebase::analytics::LogEvent( + "event_with_only_one_default"); // Changed log event name slightly + LogInfo("Logged event_with_only_one_default"); + + // If we reach here without crashing, consider the basic test passed for the + // C++ layer. Deeper verification requires platform tools. + LogInfo("SetDefaultEventParameters() tests completed."); +} + +TEST_F(FirebaseAnalyticsTest, TestClearDefaultEventParameters) { + LogInfo("Testing ClearDefaultEventParameters()."); + + // Set some defaults first. + std::map defaults; + defaults["temp_default"] = "will_be_cleared"; + firebase::analytics::SetDefaultEventParameters(defaults); + + // Now clear them all. + firebase::analytics::ClearDefaultEventParameters(); + + // Log an event - no defaults should be included. + firebase::analytics::LogEvent("event_after_clear_defaults"); + LogInfo("Logged event_after_clear_defaults"); + + // If we reach here without crashing, consider the basic test passed for the + // C++ layer. + LogInfo("ClearDefaultEventParameters() test completed."); +} + } // namespace firebase_testapp_automated diff --git a/analytics/src/analytics_android.cc b/analytics/src/analytics_android.cc index d1279c6ba7..e0fe25b009 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) @@ -518,6 +520,64 @@ void LogEvent(const char* name, const Parameter* parameters, }); } +// Convert std::map to Bundle. +jobject StringVariantMapToBundle(JNIEnv* env, + const std::map& map) { + jobject bundle = + env->NewObject(util::bundle::GetClass(), + util::bundle::GetMethodId(util::bundle::kConstructor)); + for (const auto& pair : map) { + // Bundle keys must be strings. + const char* key = pair.first.c_str(); + const Variant& value = pair.second; + // A null variant clears the default parameter for that key. + // The Android SDK uses Bundle.putString(key, null) for this. + if (value.is_null()) { + jstring key_string = env->NewStringUTF(key); + // Call Bundle.putString(key, null) + env->CallVoidMethod(bundle, + util::bundle::GetMethodId(util::bundle::kPutString), + key_string, nullptr); + util::CheckAndClearJniExceptions(env); + env->DeleteLocalRef(key_string); + } else if (!AddVariantToBundle(env, bundle, key, value)) { + LogError("SetDefaultEventParameters: Unsupported type (%s) for key %s.", + Variant::TypeName(value.type()), key); + } + // CheckAndClearJniExceptions is called within AddVariantToBundle or above + // for the null case. + } + return bundle; +} + +void SetDefaultEventParameters( + const std::map& default_parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + JNIEnv* env = g_app->GetJNIEnv(); + + jobject bundle = StringVariantMapToBundle(env, default_parameters); + + env->CallVoidMethod( + g_analytics_class_instance, + analytics::GetMethodId(analytics::kSetDefaultEventParameters), bundle); + + util::CheckAndClearJniExceptions(env); + env->DeleteLocalRef(bundle); +} + +void ClearDefaultEventParameters() { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + JNIEnv* env = g_app->GetJNIEnv(); + + // Call FirebaseAnalytics.setDefaultEventParameters(null) + env->CallVoidMethod( + g_analytics_class_instance, + analytics::GetMethodId(analytics::kSetDefaultEventParameters), + nullptr); // Pass null Bundle to clear all parameters + + util::CheckAndClearJniExceptions(env); +} + /// Initiates on-device conversion measurement given a user email address on iOS /// (no-op on Android). On iOS, requires dependency /// GoogleAppMeasurementOnDeviceConversion to be linked in, otherwise it is a diff --git a/analytics/src/analytics_ios.mm b/analytics/src/analytics_ios.mm index d56fd61356..934eff7ab3 100644 --- a/analytics/src/analytics_ios.mm +++ b/analytics/src/analytics_ios.mm @@ -312,6 +312,37 @@ void LogEvent(const char* name, const Parameter* parameters, size_t number_of_pa [FIRAnalytics logEventWithName:@(name) parameters:parameters_dict]; } +void SetDefaultEventParameters(const std::map& default_parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + // Convert the std::map to NSDictionary* + // The keys must be strings for FIRAnalytics. + NSMutableDictionary* parameters_dict = + [NSMutableDictionary dictionaryWithCapacity:default_parameters.size()]; + for (const auto& pair : default_parameters) { + NSString* key = firebase::util::StringToNSString(pair.first); + // A null Variant indicates the default parameter for that key should be + // cleared. In ObjC, setting a key to [NSNull null] in the dictionary + // achieves this. + id value = pair.second.is_null() ? [NSNull null] : firebase::util::VariantToId(pair.second); + if (value) { + [parameters_dict setObject:value forKey:key]; + } else { + // VariantToId could return nil if the variant type is unsupported. + // Log an error but continue, as NSNull case is handled above. + LogError("SetDefaultEventParameters: Failed to convert value for key %s.", + pair.first.c_str()); + } + } + + [FIRAnalytics setDefaultEventParameters:parameters_dict]; +} + +void ClearDefaultEventParameters() { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + // Passing nil to the underlying SDK method clears all parameters. + [FIRAnalytics setDefaultEventParameters:nil]; +} + /// Initiates on-device conversion measurement given a user email address on iOS (no-op on /// Android). On iOS, requires dependency GoogleAppMeasurementOnDeviceConversion to be linked /// in, otherwise it is a no-op. diff --git a/analytics/src/analytics_stub.cc b/analytics/src/analytics_stub.cc index 37c4b3520b..9d1d5ba9a9 100644 --- a/analytics/src/analytics_stub.cc +++ b/analytics/src/analytics_stub.cc @@ -97,6 +97,17 @@ void LogEvent(const char* /*name*/, const Parameter* /*parameters*/, FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); } +void SetDefaultEventParameters( + const std::map& default_parameters) { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + // No-op on stub. +} + +void ClearDefaultEventParameters() { + FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized()); + // No-op on stub. +} + /// Initiates on-device conversion measurement given a user email address on iOS /// (no-op on Android). On iOS, requires dependency /// GoogleAppMeasurementOnDeviceConversion to be linked in, otherwise it is a diff --git a/analytics/src/include/firebase/analytics.h b/analytics/src/include/firebase/analytics.h index 746df99894..78cf72c1fd 100644 --- a/analytics/src/include/firebase/analytics.h +++ b/analytics/src/include/firebase/analytics.h @@ -476,6 +476,21 @@ void LogEvent(const char* name); void LogEvent(const char* name, const Parameter* parameters, size_t number_of_parameters); +/// @brief Sets default event parameters for this app. +/// +/// This specifies parameters to be included with every subsequent call to +/// `LogEvent`, in addition to the parameters passed to `LogEvent`. +/// The same limitations apply to these parameters as are documented for +/// `LogEvent`. +/// @param[in] default_parameters Map of default parameter names and values. +/// Passing a null `Variant` value for a parameter name clears the default +/// parameter for that key. +void SetDefaultEventParameters( + const std::map& default_parameters); + +/// @brief Clears all default event parameters. +void ClearDefaultEventParameters(); + /// Initiates on-device conversion measurement given a user email address on iOS /// and tvOS (no-op on Android). On iOS and tvOS, this method requires the /// dependency GoogleAppMeasurementOnDeviceConversion to be linked in, diff --git a/release_build_files/readme.md b/release_build_files/readme.md index ae6d754b2c..cadceeb572 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -631,6 +631,11 @@ workflow use only during the development of your app, not for publicly shipping code. ## Release Notes +### Upcoming Release +- Changes + - Analytics: Add SetDefaultEventParams to set default parameters for + all LogEvent calls. + ### 12.7.0 - Changes - General (iOS): Update to Firebase Cocoapods version 11.10.0.