Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ __pycache__/

### IDE generated files
.vscode/
**/.vs/

# Unencrypted secret files
google-services.json
Expand Down
2 changes: 1 addition & 1 deletion analytics/generate_windows_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
)
parser.add_argument(
"--windows_dll",
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/google_analytics.dll"),
help="Path to the DLL file to calculate a hash."
)
parser.add_argument(
Expand Down
2 changes: 1 addition & 1 deletion analytics/integration_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ else()
)
elseif(MSVC)
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32)
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/analytics_win.dll")
set(ANALYTICS_WINDOWS_DLL "${FIREBASE_CPP_SDK_DIR}/analytics/windows/google_analytics.dll")

# For Windows, check if the Analytics DLL exists, and copy it in if so.
if (EXISTS "${ANALYTICS_WINDOWS_DLL}")
Expand Down
7 changes: 7 additions & 0 deletions analytics/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ TEST_F(FirebaseAnalyticsTest, TestLogEvents) {
"spoon_welders");
}

TEST_F(FirebaseAnalyticsTest, TestNotifyAppLifecycleChange) {
// Can't confirm that these do anything but just run them all to ensure the
// app doesn't crash.
firebase::analytics::NotifyAppLifecycleChange(firebase::analytics::kUnknown);
firebase::analytics::NotifyAppLifecycleChange(firebase::analytics::kTermination);
}

TEST_F(FirebaseAnalyticsTest, TestLogEventWithMultipleParameters) {
const firebase::analytics::Parameter kLevelUpParameters[] = {
firebase::analytics::Parameter(firebase::analytics::kParameterLevel, 5),
Expand Down
13 changes: 12 additions & 1 deletion analytics/src/analytics_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace firebase {
namespace analytics {

#if defined(_WIN32)
#define ANALYTICS_DLL_FILENAME L"analytics_win.dll"
#define ANALYTICS_DLL_FILENAME L"google_analytics.dll"

static HMODULE g_analytics_module = 0;
#endif // defined(_WIN32)
Expand Down Expand Up @@ -134,6 +134,10 @@ bool IsInitialized() { return g_initialized; }
void Terminate() {
#if defined(_WIN32)
if (g_analytics_module) {
// Make sure to notify the SDK that the analytics is being terminated to
// upload any pending data.
NotifyAppLifecycleChange(AppLifecycleState::kTermination);

FirebaseAnalytics_UnloadDynamicFunctions();
FreeLibrary(g_analytics_module);
g_analytics_module = 0;
Expand Down Expand Up @@ -381,6 +385,13 @@ void ResetAnalyticsData() {
g_fake_instance_id++;
}

// Notify the Analytics SDK about the current state of the app's lifecycle.
void NotifyAppLifecycleChange(AppLifecycleState state) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
GoogleAnalytics_NotifyAppLifecycleChange(
static_cast<GoogleAnalytics_AppLifecycleState>(state));
}

// Overloaded versions of LogEvent for convenience.

void LogEvent(const char* name) {
Expand Down
104 changes: 64 additions & 40 deletions analytics/src/analytics_desktop_dynamic.c

Large diffs are not rendered by default.

66 changes: 56 additions & 10 deletions analytics/src/analytics_desktop_dynamic.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ typedef struct GoogleAnalytics_Reserved_Opaque GoogleAnalytics_Reserved;
* using GoogleAnalytics_Options_Create(), initialization will fail, and the
* caller will be responsible for destroying the options.
*/
ANALYTICS_API typedef struct {
typedef struct ANALYTICS_API GoogleAnalytics_Options {
/**
* @brief The unique identifier for the Firebase app across all of Firebase
* with a platform-specific format. This is a required field, can not be null
Expand Down Expand Up @@ -68,12 +68,54 @@ ANALYTICS_API typedef struct {
*/
bool analytics_collection_enabled_at_first_launch;

/**
* @brief An optional path to a folder where the SDK can store its data.
* If not provided, the SDK will store its data in the same folder as the
* executable.
*
* The path must pre-exist and the app has read and write access to it.
*/
const char* app_data_directory;

/**
* @brief Reserved for internal use by the SDK.
*/
GoogleAnalytics_Reserved* reserved;
} GoogleAnalytics_Options;

/**
* @brief The state of an app in its lifecycle.
*/
typedef enum GoogleAnalytics_AppLifecycleState {
/**
* @brief This is an invalid state that is used to capture unininitialized
* values.
*/
GoogleAnalytics_AppLifecycleState_kUnknown = 0,
/**
* @brief The app is about to be terminated.
*/
GoogleAnalytics_AppLifecycleState_kTermination = 1,
} GoogleAnalytics_AppLifecycleState;

/**
* @brief The log level of a log message.
*/
typedef enum GoogleAnalytics_LogLevel {
kDebug,
kInfo,
kWarning,
kError,
} GoogleAnalytics_LogLevel;

/**
* @brief Function pointer type for a log callback.
*
* @param[in] message The log message string.
*/
typedef void (*GoogleAnalytics_LogCallback)(GoogleAnalytics_LogLevel log_level,
const char* message);

/**
* @brief Creates an instance of GoogleAnalytics_Options with default values.
*
Expand Down Expand Up @@ -149,25 +191,27 @@ extern "C" {
extern GoogleAnalytics_Options* (*ptr_GoogleAnalytics_Options_Create)();
extern void (*ptr_GoogleAnalytics_Options_Destroy)(GoogleAnalytics_Options* options);
extern GoogleAnalytics_Item* (*ptr_GoogleAnalytics_Item_Create)();
extern void (*ptr_GoogleAnalytics_Item_InsertInt)(GoogleAnalytics_Item* item, const char* key, int64_t value);
extern void (*ptr_GoogleAnalytics_Item_InsertDouble)(GoogleAnalytics_Item* item, const char* key, double value);
extern void (*ptr_GoogleAnalytics_Item_InsertString)(GoogleAnalytics_Item* item, const char* key, const char* value);
extern bool (*ptr_GoogleAnalytics_Item_InsertInt)(GoogleAnalytics_Item* item, const char* key, int64_t value);
extern bool (*ptr_GoogleAnalytics_Item_InsertDouble)(GoogleAnalytics_Item* item, const char* key, double value);
extern bool (*ptr_GoogleAnalytics_Item_InsertString)(GoogleAnalytics_Item* item, const char* key, const char* value);
extern void (*ptr_GoogleAnalytics_Item_Destroy)(GoogleAnalytics_Item* item);
extern GoogleAnalytics_ItemVector* (*ptr_GoogleAnalytics_ItemVector_Create)();
extern void (*ptr_GoogleAnalytics_ItemVector_InsertItem)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item);
extern bool (*ptr_GoogleAnalytics_ItemVector_InsertItem)(GoogleAnalytics_ItemVector* item_vector, GoogleAnalytics_Item* item);
extern void (*ptr_GoogleAnalytics_ItemVector_Destroy)(GoogleAnalytics_ItemVector* item_vector);
extern GoogleAnalytics_EventParameters* (*ptr_GoogleAnalytics_EventParameters_Create)();
extern void (*ptr_GoogleAnalytics_EventParameters_InsertInt)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertDouble)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertString)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value);
extern void (*ptr_GoogleAnalytics_EventParameters_InsertItemVector)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertInt)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, int64_t value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertDouble)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, double value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertString)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, const char* value);
extern bool (*ptr_GoogleAnalytics_EventParameters_InsertItemVector)(GoogleAnalytics_EventParameters* event_parameter_map, const char* key, GoogleAnalytics_ItemVector* value);
extern void (*ptr_GoogleAnalytics_EventParameters_Destroy)(GoogleAnalytics_EventParameters* event_parameter_map);
extern bool (*ptr_GoogleAnalytics_Initialize)(const GoogleAnalytics_Options* options);
extern bool (*ptr_GoogleAnalytics_Initialize)(GoogleAnalytics_Options* options);
extern void (*ptr_GoogleAnalytics_LogEvent)(const char* name, GoogleAnalytics_EventParameters* parameters);
extern void (*ptr_GoogleAnalytics_SetUserProperty)(const char* name, const char* value);
extern void (*ptr_GoogleAnalytics_SetUserId)(const char* user_id);
extern void (*ptr_GoogleAnalytics_ResetAnalyticsData)();
extern void (*ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled)(bool enabled);
extern void (*ptr_GoogleAnalytics_SetLogCallback)(GoogleAnalytics_LogCallback callback);
extern void (*ptr_GoogleAnalytics_NotifyAppLifecycleChange)(GoogleAnalytics_AppLifecycleState state);

#define GoogleAnalytics_Options_Create ptr_GoogleAnalytics_Options_Create
#define GoogleAnalytics_Options_Destroy ptr_GoogleAnalytics_Options_Destroy
Expand All @@ -191,6 +235,8 @@ extern void (*ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled)(bool enabled);
#define GoogleAnalytics_SetUserId ptr_GoogleAnalytics_SetUserId
#define GoogleAnalytics_ResetAnalyticsData ptr_GoogleAnalytics_ResetAnalyticsData
#define GoogleAnalytics_SetAnalyticsCollectionEnabled ptr_GoogleAnalytics_SetAnalyticsCollectionEnabled
#define GoogleAnalytics_SetLogCallback ptr_GoogleAnalytics_SetLogCallback
#define GoogleAnalytics_NotifyAppLifecycleChange ptr_GoogleAnalytics_NotifyAppLifecycleChange
// clang-format on

// Number of Google Analytics functions expected to be loaded from the DLL.
Expand Down
25 changes: 25 additions & 0 deletions analytics/src/include/firebase/analytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,31 @@ void SetSessionTimeoutDuration(int64_t milliseconds);
/// instance id.
void ResetAnalyticsData();


/// @brief The state of an app in its lifecycle.
///
/// kUnknown is an invalid sate that is used to capture uninitialized values.
/// kTermination is used to indicate that the app is about to be terminated.
enum AppLifecycleState {
kUnknown = 0,
kTermination
};

/// @brief Notifies the current state of the app's lifecycle.
///
/// This method is used to notify the Analytics SDK about the current state of
/// the app's lifecycle. The Analytics SDK will use this information to log
/// events, update user properties, upload data, etc.
///
/// kTermination is used to indicate that the app is about to be terminated.
/// The caller will be blocked until all pending data is uploaded or an error
/// occurs. The caller must ensure the OS does not terminate background threads
/// before the call returns.
///
/// @param[in] state The current state of the app's lifecycle.

void NotifyAppLifecycleChange(AppLifecycleState state);

/// Get the instance ID from the analytics service.
///
/// @note This is *not* the same ID as the ID returned by
Expand Down
Binary file removed analytics/windows/analytics_win.dll
Binary file not shown.
Binary file added analytics/windows/google_analytics.dll
Binary file not shown.
141 changes: 141 additions & 0 deletions analytics/windows/include/public/analytics.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ANALYTICS_MOBILE_CONSOLE_MEASUREMENT_PUBLIC_ANALYTICS_H_
#define ANALYTICS_MOBILE_CONSOLE_MEASUREMENT_PUBLIC_ANALYTICS_H_

#include <cstdint>
#include <functional>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -33,6 +47,41 @@ class Analytics {
std::variant<int64_t, double, std::string, ItemVector>;
using EventParameters = std::unordered_map<std::string, EventParameterValue>;

/**
* @brief The state of an app in its lifecycle.
*/
enum AppLifecycleState {
/**
* @brief This is an invalid state that is used to capture unininitialized
* values.
*/
kUnknown,
/**
* @brief The app is about to be terminated.
*/
kTermination,
};

/**
* @brief The log level of the message logged by the SDK.
*/
enum LogLevel {
kDebug,
kInfo,
kWarning,
kError,
};

/**
* @brief The callback type for logging messages from the SDK.
*
* The callback is invoked whenever the SDK logs a message.
*
* @param[in] log_level The log level of the message.
* @param[in] message The message logged by the SDK.
*/
using LogCallback = std::function<void(LogLevel, const std::string&)>;

/**
* @brief Options for initializing the Analytics SDK.
*/
Expand Down Expand Up @@ -63,6 +112,15 @@ class Analytics {
* point.
*/
bool analytics_collection_enabled_at_first_launch = true;

/**
* @brief An optional path to a folder where the SDK can store its data.
* If not provided, the SDK will store its data in the same folder as the
* executable.
*
* The path must pre-exist and the app has read and write access to it.
*/
std::optional<std::string> app_data_directory;
};

/**
Expand Down Expand Up @@ -96,6 +154,10 @@ class Analytics {
google_analytics_options->package_name = options.package_name.c_str();
google_analytics_options->analytics_collection_enabled_at_first_launch =
options.analytics_collection_enabled_at_first_launch;
google_analytics_options->app_data_directory =
options.app_data_directory.value_or("").empty()
? nullptr
: options.app_data_directory.value().c_str();
return GoogleAnalytics_Initialize(google_analytics_options);
}

Expand Down Expand Up @@ -269,8 +331,87 @@ class Analytics {
GoogleAnalytics_SetAnalyticsCollectionEnabled(enabled);
}

/**
* @brief Allows the passing of a callback to be used when the SDK logs any
* messages regarding its behavior. The callback must be thread-safe.
*
* @param[in] callback The callback to use. Must be thread-safe.
*/
void SetLogCallback(LogCallback callback) {
{
std::lock_guard<std::mutex> lock(mutex_);
current_callback_ = callback;
}

if (!callback) {
GoogleAnalytics_SetLogCallback(nullptr);
return;
}

GoogleAnalytics_SetLogCallback(
[](GoogleAnalytics_LogLevel log_level, const char* message) {
LogLevel cpp_log_level;
switch (log_level) {
case GoogleAnalytics_LogLevel::kDebug:
cpp_log_level = LogLevel::kDebug;
break;
case GoogleAnalytics_LogLevel::kInfo:
cpp_log_level = LogLevel::kInfo;
break;
case GoogleAnalytics_LogLevel::kWarning:
cpp_log_level = LogLevel::kWarning;
break;
case GoogleAnalytics_LogLevel::kError:
cpp_log_level = LogLevel::kError;
break;
default:
cpp_log_level = LogLevel::kInfo;
}
LogCallback local_callback;
Analytics& self = Analytics::GetInstance();
{
std::lock_guard<std::mutex> lock(self.mutex_);
local_callback = self.current_callback_;
}
if (local_callback) {
local_callback(cpp_log_level, std::string(message));
}
});
}

/**
* @brief Notifies the current state of the app's lifecycle.
*
* This method is used to notify the Analytics SDK about the current state of
* the app's lifecycle. The Analytics SDK will use this information to log
* events, update user properties, upload data, etc.
*
* kTermination is used to indicate that the app is about to be terminated.
* The caller will be blocked until all pending data is uploaded or an error
* occurs. The caller must ensure the OS does not terminate background threads
* before the call returns.
*
* @param[in] state The current state of the app's lifecycle.
*/
void NotifyAppLifecycleChange(AppLifecycleState state) {
GoogleAnalytics_AppLifecycleState c_state;
switch (state) {
case AppLifecycleState::kTermination:
c_state = GoogleAnalytics_AppLifecycleState_kTermination;
break;
case AppLifecycleState::kUnknown:
default:
c_state = GoogleAnalytics_AppLifecycleState_kUnknown;
break;
}
GoogleAnalytics_NotifyAppLifecycleChange(c_state);
}

private:
Analytics() = default;

std::mutex mutex_;
LogCallback current_callback_;
};

} // namespace google::analytics
Expand Down
Loading
Loading