1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- #include " analytics/src/windows/analytics_windows .h"
15+ #include " analytics/src/windows/analytics_dynamic .h"
1616#include " app/src/include/firebase/app.h"
1717#include " analytics/src/include/firebase/analytics.h"
18- #include " analytics/src/common/ analytics_common.h"
19- #include " common /src/include/firebase/variant.h"
18+ #include " analytics/src/analytics_common.h"
19+ #include " app /src/include/firebase/variant.h"
2020#include " app/src/include/firebase/future.h"
2121#include " app/src/include/firebase/log.h"
2222#include " app/src/future_manager.h" // For FutureData
2323
2424#include < vector>
2525#include < string>
2626#include < map>
27+ #include < sstream>
28+
29+ #if defined(_WIN32)
30+ #include < windows.h>
31+ #endif // defined(_WIN32)
2732
2833namespace firebase {
2934namespace analytics {
3035
36+ #if defined(_WIN32)
37+ #define ANALYTICS_DLL_DEFAULT_FILENAME " analytics_win.dll"
38+ const char *g_analytics_dll_filename = nullptr ;
39+ static HMODULE g_analytics_dll = 0 ;
40+
41+ void SetAnalyticsLibraryPath (const char * path) {
42+ if (g_analytics_dll_filename) {
43+ delete g_analytics_dll_filename;
44+ g_analytics_dll_filename = nullptr ;
45+ }
46+ if (path) {
47+ g_analytics_dll_filename = new char [strlen (path)+1 ];
48+ strcpy (g_analytics_dll_filename, path);
49+ }
50+ }
51+ #endif
52+
3153// Future data for analytics.
3254// This is initialized in `Initialize()` and cleaned up in `Terminate()`.
33- static FutureData* g_future_data = nullptr ;
55+ static bool g_initialized = false ;
56+ static int g_fake_instance_id = 0 ;
3457
3558// Initializes the Analytics desktop API.
3659// This function must be called before any other Analytics methods.
@@ -40,22 +63,48 @@ void Initialize(const App& app) {
4063 // with other Firebase platforms.
4164 (void )app;
4265
43- if (g_future_data) {
44- LogWarning (" Analytics: Initialize() called when already initialized." );
45- } else {
46- g_future_data = new FutureData (internal::kAnalyticsFnCount );
66+ g_initialized = true ;
67+ internal::RegisterTerminateOnDefaultAppDestroy ();
68+ internal::FutureData::Create ();
69+ g_fake_instance_id = 0 ;
70+
71+ #if defined(_WIN32)
72+ if (!g_analytics_dll) {
73+ const char * dll_filename = g_analytics_dll_filename;
74+ if (!dll_filename) dll_filename = ANALYTICS_DLL_DEFAULT_FILENAME;
75+ auto wFilename = toUtf16 (dll_filename);
76+ g_analytics_dll = LoadLibraryW (wFilename);
77+ if (g_analytics_dll) {
78+ LogInfo (" Successfully loaded Analytics DLL %s" , g_analytics_dll_filename);
79+ } else {
80+ LogError (" Failed to load Analytics DLL %s" , g_analytics_dll_filename);
81+ }
4782 }
83+ FirebaseAnalytics_LoadAnalyticsFunctions (g_analytics_dll);
84+ #endif
4885}
4986
87+ namespace internal {
88+
89+ // Determine whether the analytics module is initialized.
90+ bool IsInitialized () { return g_initialized; }
91+
92+ } // namespace internal
93+
5094// Terminates the Analytics desktop API.
5195// Call this function when Analytics is no longer needed to free up resources.
5296void Terminate () {
53- if (g_future_data) {
54- delete g_future_data ;
55- g_future_data = nullptr ;
56- } else {
57- LogWarning ( " Analytics: Terminate() called when not initialized or already terminated. " ) ;
97+ # if defined(_WIN32)
98+ FirebaseAnalytics_UnloadAnalyticsFunctions () ;
99+ if (g_analytics_dll) {
100+ FreeLibrary (g_analytics_dll);
101+ g_analytics_dll = 0 ;
58102 }
103+ #endif
104+
105+ internal::FutureData::Destroy ();
106+ internal::UnregisterTerminateOnDefaultAppDestroy ();
107+ g_initialized = false ;
59108}
60109
61110static void ConvertParametersToGAParams (
@@ -95,8 +144,8 @@ static void ConvertParametersToGAParams(
95144 // is set as the property's value.
96145 // All these GoogleAnalytics_Items are then bundled into a single
97146 // GoogleAnalytics_ItemVector, which is associated with the original parameter's name.
98- const std::map<std::string , firebase::Variant>& user_map =
99- param.value .map_value ();
147+ const std::map<firebase::Variant , firebase::Variant>& user_map =
148+ param.value .map ();
100149 if (user_map.empty ()) {
101150 LogWarning (" Analytics: Parameter '%s' is an empty map. Skipping." , param.name );
102151 continue ; // Skip this parameter
@@ -111,7 +160,12 @@ static void ConvertParametersToGAParams(
111160
112161 bool item_vector_populated = false ;
113162 for (const auto & entry : user_map) {
114- const std::string& key_from_map = entry.first ;
163+ const firebase::Variant& key_variant = entry.first ;
164+ if (!key_variant.is_string ()) {
165+ LogError (" Analytics: Non-string map key found. Skipping." );
166+ continue ;
167+ }
168+ const std::string& key_from_map = key_variant.mutable_string ();
115169 const firebase::Variant& value_from_map = entry.second ;
116170
117171 GoogleAnalytics_Item* c_item = GoogleAnalytics_Item_Create ();
@@ -164,6 +218,8 @@ static void ConvertParametersToGAParams(
164218void LogEvent (const char * name,
165219 const Parameter* parameters,
166220 size_t number_of_parameters) {
221+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
222+
167223 if (name == nullptr || name[0 ] == ' \0 ' ) {
168224 LogError (" Analytics: Event name cannot be null or empty." );
169225 return ;
@@ -196,6 +252,8 @@ void LogEvent(const char* name,
196252// characters long. Setting the value to `nullptr` or an empty string will
197253// clear the user property. Must be UTF-8 encoded if not nullptr.
198254void SetUserProperty (const char * name, const char * property) {
255+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
256+
199257 if (name == nullptr || name[0 ] == ' \0 ' ) {
200258 LogError (" Analytics: User property name cannot be null or empty." );
201259 return ;
@@ -216,6 +274,7 @@ void SetUserProperty(const char* name, const char* property) {
216274// characters long, and UTF-8 encoded. Setting user_id to `nullptr` removes
217275// the user ID.
218276void SetUserId (const char * user_id) {
277+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
219278 // The C API GoogleAnalytics_SetUserId allows user_id to be nullptr to clear the user ID.
220279 // The C API documentation also mentions: "The user ID must be non-empty and
221280 // no more than 256 characters long".
@@ -235,18 +294,24 @@ void SetUserId(const char* user_id) {
235294//
236295// @param[in] enabled A flag that enables or disables Analytics collection.
237296void SetAnalyticsCollectionEnabled (bool enabled) {
297+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
298+
238299 GoogleAnalytics_SetAnalyticsCollectionEnabled (enabled);
239300}
240301
241302// Clears all analytics data for this app from the device and resets the app
242303// instance ID.
243304void ResetAnalyticsData () {
305+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
306+
244307 GoogleAnalytics_ResetAnalyticsData ();
245308}
246309
247310// --- Stub Implementations for Unsupported Features ---
248311
249312void SetConsent (const std::map<ConsentType, ConsentStatus>& consent_settings) {
313+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
314+
250315 // Not supported by the Windows C API.
251316 (void )consent_settings; // Mark as unused
252317 LogWarning (" Analytics: SetConsent() is not supported and has no effect on Desktop." );
@@ -298,75 +363,79 @@ void LogEvent(const char* name, const char* parameter_name,
298363
299364void InitiateOnDeviceConversionMeasurementWithEmailAddress (
300365 const char * email_address) {
366+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
301367 (void )email_address;
302368 LogWarning (" Analytics: InitiateOnDeviceConversionMeasurementWithEmailAddress() is not supported and has no effect on Desktop." );
303369}
304370
305371void InitiateOnDeviceConversionMeasurementWithPhoneNumber (
306372 const char * phone_number) {
373+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
307374 (void )phone_number;
308375 LogWarning (" Analytics: InitiateOnDeviceConversionMeasurementWithPhoneNumber() is not supported and has no effect on Desktop." );
309376}
310377
311378void InitiateOnDeviceConversionMeasurementWithHashedEmailAddress (
312379 std::vector<unsigned char > hashed_email_address) {
380+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
313381 (void )hashed_email_address;
314382 LogWarning (" Analytics: InitiateOnDeviceConversionMeasurementWithHashedEmailAddress() is not supported and has no effect on Desktop." );
315383}
316384
317385void InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber (
318386 std::vector<unsigned char > hashed_phone_number) {
387+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
319388 (void )hashed_phone_number;
320389 LogWarning (" Analytics: InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber() is not supported and has no effect on Desktop." );
321390}
322391
323392void SetSessionTimeoutDuration (int64_t milliseconds) {
393+ FIREBASE_ASSERT_RETURN_VOID (internal::IsInitialized ());
324394 (void )milliseconds;
325395 LogWarning (" Analytics: SetSessionTimeoutDuration() is not supported and has no effect on Desktop." );
326396}
327397
328398Future<std::string> GetAnalyticsInstanceId () {
329- LogWarning (" Analytics: GetAnalyticsInstanceId() is not supported on Desktop." );
330- if (!g_future_data) {
331- LogError (" Analytics: API not initialized; call Initialize() first." );
332- static firebase::Future<std::string> invalid_future; // Default invalid
333- if (!g_future_data) return invalid_future; // Or some other error future
399+ FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
400+ auto * api = internal::FutureData::Get ()->api ();
401+ const auto future_handle =
402+ api->SafeAlloc <std::string>(internal::kAnalyticsFnGetAnalyticsInstanceId );
403+ std::string instance_id = std::string (" FakeAnalyticsInstanceId" );
404+ {
405+ std::stringstream ss;
406+ ss << g_fake_instance_id;
407+ instance_id += ss.str ();
334408 }
335- const auto handle =
336- g_future_data->CreateFuture (internal::kAnalyticsFn_GetAnalyticsInstanceId , nullptr );
337- g_future_data->CompleteFuture (handle, 0 /* error_code */ , nullptr /* error_message_string */ );
338- return g_future_data->GetFuture <std::string>(handle);
409+ api->CompleteWithResult (future_handle, 0 , " " , instance_id);
410+ LogWarning (" Analytics: GetAnalyticsInstanceId() is not supported on Desktop." );
411+ return Future<std::string>(api, future_handle.get ());
339412}
340413
341414Future<std::string> GetAnalyticsInstanceIdLastResult () {
342- if (!g_future_data) {
343- LogError (" Analytics: API not initialized; call Initialize() first." );
344- static firebase::Future<std::string> invalid_future;
345- return invalid_future;
346- }
347- return g_future_data->LastResult <std::string>(internal::kAnalyticsFn_GetAnalyticsInstanceId );
415+ FIREBASE_ASSERT_RETURN (Future<std::string>(), internal::IsInitialized ());
416+ LogWarning (" Analytics: GetAnalyticsInstanceIdLastResult() is not supported on Desktop." );
417+ return static_cast <const Future<std::string>&>(
418+ internal::FutureData::Get ()->api ()->LastResult (
419+ internal::kAnalyticsFnGetAnalyticsInstanceId ));
348420}
349421
350422Future<int64_t > GetSessionId () {
423+ FIREBASE_ASSERT_RETURN (Future<int64_t >(), internal::IsInitialized ());
424+ auto * api = internal::FutureData::Get ()->api ();
425+ const auto future_handle =
426+ api->SafeAlloc <int64_t >(internal::kAnalyticsFnGetSessionId );
427+ int64_t session_id = 0x5E5510171D570BL ; // "SESSIONIDSTUB", kinda
428+ api->CompleteWithResult (future_handle, 0 , " " , session_id);
351429 LogWarning (" Analytics: GetSessionId() is not supported on Desktop." );
352- if (!g_future_data) {
353- LogError (" Analytics: API not initialized; call Initialize() first." );
354- static firebase::Future<int64_t > invalid_future;
355- return invalid_future;
356- }
357- const auto handle =
358- g_future_data->CreateFuture (internal::kAnalyticsFn_GetSessionId , nullptr );
359- g_future_data->CompleteFuture (handle, 0 /* error_code */ , nullptr /* error_message_string */ );
360- return g_future_data->GetFuture <int64_t >(handle);
430+ return Future<int64_t >(api, future_handle.get ());
361431}
362432
363433Future<int64_t > GetSessionIdLastResult () {
364- if (!g_future_data) {
365- LogError (" Analytics: API not initialized; call Initialize() first." );
366- static firebase::Future<int64_t > invalid_future;
367- return invalid_future;
368- }
369- return g_future_data->LastResult <int64_t >(internal::kAnalyticsFn_GetSessionId );
434+ FIREBASE_ASSERT_RETURN (Future<int64_t >(), internal::IsInitialized ());
435+ LogWarning (" Analytics: GetSessionIdLastResult() is not supported on Desktop." );
436+ return static_cast <const Future<int64_t >&>(
437+ internal::FutureData::Get ()->api ()->LastResult (
438+ internal::kAnalyticsFnGetSessionId ));
370439}
371440
372441} // namespace analytics
0 commit comments