diff --git a/.github/workflows/bob.yml b/.github/workflows/bob.yml index 7f9fb75..2e71f8f 100644 --- a/.github/workflows/bob.yml +++ b/.github/workflows/bob.yml @@ -43,7 +43,7 @@ jobs: build_with_bob_macos: strategy: matrix: - platform: [arm64-darwin, x86_64-darwin] + platform: [arm64-ios, x86_64-darwin] runs-on: macOS-latest name: Build diff --git a/.gitignore b/.gitignore index e143b1f..d54478c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ google-services.xml dependencies.json GoogleService-Info.plist Info.plist +/debug.keystore +/debug.keystore.pass.txt +/manifest.private.der +/manifest.public.der diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2e261a8..0e68934 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1 +1,4 @@ Follow guide in https://github.com/defold/extension-firebase + +Download Firebase C++ SDK download link: https://firebase.google.com/download/cpp +Copy `firebase_cpp_sdk/include/firebase/analytics/*.*` to `include/firebase/analytics/` in extension. Make sure new constant registered in Lua. \ No newline at end of file diff --git a/README.md b/README.md index 3a78cc1..ae38501 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ [![Actions Status Alpha](https://github.com/defold/extension-firebase-analytics/actions/workflows/bob.yml/badge.svg)](https://github.com/defold/extension-firebase-analytics/actions) -![](https://img.shields.io/badge/Firebase%20CPP%20SDK-8.10.0-green) -![](https://img.shields.io/badge/Firebase%20iOS%20SDK-8.13.0-green) - # Firebase Analytics for Defold Defold [native extension](https://www.defold.com/manuals/extensions/) that integrates [Firebase Analytics](https://firebase.google.com/docs/analytics) functionality on Android and iOS. diff --git a/docs/add-dependency.png b/docs/add-dependency.png new file mode 100644 index 0000000..a920abd Binary files /dev/null and b/docs/add-dependency.png differ diff --git a/docs/index.md b/docs/index.md index 51ceea9..a27662c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,52 +9,64 @@ This extension allows you to interact with Firebase Analytics in a uniform way f ## Installation -To use this library in your Defold project, add the following URLs to your `game.project` dependencies: +To use Firabase in your Defold project, add a version of the Firebase extension to your `game.project` dependencies from the list of available [Firebase Releases](https://github.com/defold/extension-firebase/releases) and corresponding [Firebase Config Release](https://github.com/defold/extension-firebase-remoteconfig/releases). +Find the version you want for both extensions, copy the URLs to ZIP archive of the release and add it to the project dependencies. - -| Firebase C++ SDK | Firebase iOS SDK | Dependencies | -|-------------------------|-------------------------|--------------| -| Firebase C++ SDK 8.10.0 | Firebase iOS SDK 8.13.0 |[https://github.com/defold/extension-firebase/archive/refs/tags/1.4.2.zip](https://github.com/defold/extension-firebase/archive/refs/tags/1.4.2.zip)
[https://github.com/defold/extension-firebase-analytics/archive/refs/tags/2.2.0.zip](https://github.com/defold/extension-firebase-analytics/archive/refs/tags/2.2.2.zip) | +![](add-dependency.png) ## Setup Follow the [main setup guide for integration of Firebase in Defold](https://www.defold.com/extension-firebase). - ## Usage ```lua -function init(self) - -- use firebase only if it is supported on the current platform - if not firebase then +local function firebase_analytics_callback(self, message_id, message) + if message_id == remoteconfig.MSG_ERROR then + -- an error was detected when performing an analytics config operation + print("Firebase Analytics Config error: ", message.error) return end - -- initialise firebase and check that it was successful - local ok, err = firebase.init() - if not ok then - print(err) + if message_id == remoteconfig.MSG_INSTANCE_ID then + -- result of the firebase.analytics.get_id() call + print("Firebase Analytics Config instance_id: ", message.instance_id) return end +end - -- initialise analytics - firebase.analytics.init() - - -- log data - firebase.analytics.set_screen("myscreen", "collection") - firebase.analytics.log_string("character", "storm trooper") - firebase.analytics.log_int("kills", 152) - firebase.analytics.log_number("speed", 1.15) - local t = { - number = math.random(1,100), - boolean = true, - string = "some_string" - } - firebase.analytics.log_table("stats", t) +function init(self) + -- use firebase only if it is supported on the current platform + if not firebase then + return + end + + -- initialise firebase and check that it was successful + firebase.set_callback(function(self, message_id, message) + if message_id == firebase.MSG_INITIALIZED then + firebase.remoteconfig.set_callback(firebase_analytics_callback) + firebase.analytics.initialize() + + -- log data + firebase.analytics.log_string("character", "storm trooper") + firebase.analytics.log_int("kills", 152) + firebase.analytics.log("tutorial_done") + firebase.analytics.log_number("speed", 1.15) + local t = { + number = math.random(1,100), + boolean = true, + string = "some_string" + } + firebase.analytics.log_table("stats", t) + firebase.analytics.get_id() + end + end) end ``` +Read about [events](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event) and [parameters](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param) naming limitations in the official documentation. + ## Source code The source code is available on [GitHub](https://github.com/defold/extension-firebase-analytics) diff --git a/example/analytics.gui b/example/analytics.gui index d312577..9ec9575 100644 --- a/example/analytics.gui +++ b/example/analytics.gui @@ -43,6 +43,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -99,6 +101,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -163,6 +169,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -202,6 +212,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -258,6 +270,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -322,6 +338,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -361,6 +381,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -417,6 +439,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -481,6 +507,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -520,6 +550,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -576,6 +608,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -640,6 +676,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -679,6 +719,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -735,6 +777,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -799,6 +845,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -832,12 +882,14 @@ nodes { w: 1.0 } type: TYPE_TEMPLATE - id: "set_screen" + id: "set_enabled" layer: "" inherit_alpha: true alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -873,12 +925,12 @@ nodes { type: TYPE_BOX blend_mode: BLEND_MODE_ALPHA texture: "dirtylarry/button_normal" - id: "set_screen/bg" + id: "set_enabled/bg" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE pivot: PIVOT_CENTER adjust_mode: ADJUST_MODE_FIT - parent: "set_screen" + parent: "set_enabled" layer: "below" inherit_alpha: true slice9 { @@ -894,6 +946,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -928,9 +984,9 @@ nodes { } type: TYPE_TEXT blend_mode: BLEND_MODE_ALPHA - text: "set_screen()" + text: "set_enabled()" font: "dirtylarry" - id: "set_screen/label" + id: "set_enabled/label" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE pivot: PIVOT_CENTER @@ -948,7 +1004,7 @@ nodes { } adjust_mode: ADJUST_MODE_FIT line_break: false - parent: "set_screen/bg" + parent: "set_enabled/bg" layer: "text" inherit_alpha: true alpha: 1.0 @@ -958,6 +1014,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -997,6 +1057,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -1053,6 +1115,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -1117,6 +1183,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -1156,6 +1226,8 @@ nodes { alpha: 1.0 template: "/gooey/themes/dirtylarry/components/button.gui" template_node_child: false + custom_type: 0 + enabled: true } nodes { position { @@ -1212,6 +1284,10 @@ nodes { overridden_fields: 4 template_node_child: true size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -1276,6 +1352,10 @@ nodes { template_node_child: true text_leading: 1.0 text_tracking: 0.0 + custom_type: 0 + enabled: true + visible: true + material: "" } nodes { position { @@ -1328,6 +1408,10 @@ nodes { alpha: 1.0 template_node_child: false size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true + material: "" } layers { name: "below" diff --git a/example/analytics.gui_script b/example/analytics.gui_script index 8b9ff18..0cff96d 100644 --- a/example/analytics.gui_script +++ b/example/analytics.gui_script @@ -3,102 +3,112 @@ local dirtylarry = require "gooey.themes.dirtylarry.dirtylarry" local STRINGS = { "Yoda", "Luke", "Leia", "Han-Solo", "Darth Vader" } local SCREENS = { - "A New Hope", - "The Empire Strikes Back", - "Return of the Jedi", - "Phantom Menace", - "Attack of the Clones", - "Revenge of the Sith", - "The Force Awakens", - "The Last Jedi" + "A New Hope", + "The Empire Strikes Back", + "Return of the Jedi", + "Phantom Menace", + "Attack of the Clones", + "Revenge of the Sith", + "The Force Awakens", + "The Last Jedi" } local function random(t) - return t[math.random(1, #t)] + return t[math.random(1, #t)] +end + +local function analytics_callback(self, message_id, message) + if message_id == analytics.MSG_ERROR then + print("ERROR: "..message.error) + elseif message_id == analytics.MSG_INSTANCE_ID then + print("Instance ID: "..message.instance_id) + end end function init(self) - msg.post(".", "acquire_input_focus") - math.randomseed(os.time()) math.random() - if firebase then - firebase.init() - end - if analytics then - analytics.init() - end - gui.animate(gui.get_node("pie"), "rotation.z", 360, gui.EASING_LINEAR, 2, 0, function() end, gui.PLAYBACK_LOOP_FORWARD) + msg.post(".", "acquire_input_focus") + math.randomseed(os.time()) math.random() + if firebase then + firebase.set_callback(function(self, message_id, message) + if message_id == firebase.MSG_INITIALIZED then + if analytics then + analytics.set_callback(analytics_callback) + analytics.initialize() + analytics.get_id() + end + end + end) + firebase.initialize() + end + gui.animate(gui.get_node("pie"), "rotation.z", 360, gui.EASING_LINEAR, 2, 0, function() end, gui.PLAYBACK_LOOP_FORWARD) end function on_input(self, action_id, action) - dirtylarry.button("set_screen", action_id, action, function() - local s = random(SCREENS):gsub(" ", "_") - print("set_screen()", s) - if analytics then - analytics.set_screen(s, "analytics.collection") - end - end) - dirtylarry.button("set_user_id", action_id, action, function() - local id = "user_" .. random(STRINGS) .. "_" .. tostring(os.time()) - print("set_user_id()", id) - if analytics then - analytics.set_user_id(id) - end - end) - dirtylarry.button("set_user_property", action_id, action, function() - local property = "prop1" - local value = "value" .. random(STRINGS) - print("set_user_property()", property, value) - if analytics then - analytics.set_user_property(property, value) - end - end) - - dirtylarry.button("log", action_id, action, function() - local s = random(STRINGS):gsub(" ", "_") - print("log()", s) - if analytics then - analytics.log(s) - end - end) - dirtylarry.button("log_string", action_id, action, function() - local s = random(STRINGS):gsub(" ", "_") - print("log_string()", s) - if analytics then - analytics.log_string("some_event1", "some_string", s) - end - end) - dirtylarry.button("log_number", action_id, action, function() - local n = math.random() - print("log_number()", n) - if analytics then - analytics.log_number("some_event2", "some_number", n) - end - end) - dirtylarry.button("log_int", action_id, action, function() - local i = math.random(1, 10000) - print("log_int()", i) - if analytics then - analytics.log_int(analytics.EVENT_LEVELUP, analytics.PARAM_LEVEL, i) - end - end) - - dirtylarry.button("log_table", action_id, action, function() - local tbl = { - [analytics.PARAM_SCORE] = 100, - [analytics.PARAM_LEVEL] = 1, - [analytics.PARAM_CHARACTER] = "Yoda", - some_param1 = math.random(1,100), - some_param2 = true, - some_param3 = "some_string" - } - pprint("log_table()", tbl) - if analytics then - analytics.log_table(analytics.EVENT_POSTSCORE, tbl) - end - end) -end + dirtylarry.button("set_enabled", action_id, action, function() + local enabled = not self.analytics_enabled + self.analytics_enabled = enabled + print("set_enabled()", enabled) + if analytics then + analytics.set_enabled(enabled) + end + end) + dirtylarry.button("set_user_id", action_id, action, function() + local id = "user_" .. random(STRINGS) .. "_" .. tostring(os.time()) + print("set_user_id()", id) + if analytics then + analytics.set_user_id(id) + end + end) + dirtylarry.button("set_user_property", action_id, action, function() + local property = "prop1" + local value = "value" .. random(STRINGS) + print("set_user_property()", property, value) + if analytics then + analytics.set_user_property(property, value) + end + end) -function on_reload(self) - -- Add input-handling code here - -- Remove this function if not needed + dirtylarry.button("log", action_id, action, function() + local s = random(STRINGS):gsub(" ", "_") + print("log()", s) + if analytics then + analytics.log(s) + end + end) + dirtylarry.button("log_string", action_id, action, function() + local s = random(STRINGS):gsub(" ", "_") + print("log_string()", s) + if analytics then + analytics.log_string("some_event1", "some_string", s) + end + end) + dirtylarry.button("log_number", action_id, action, function() + local n = math.random() + print("log_number()", n) + if analytics then + analytics.log_number("some_event2", "some_number", n) + end + end) + dirtylarry.button("log_int", action_id, action, function() + local i = math.random(1, 10000) + print("log_int()", i) + if analytics then + analytics.log_int(analytics.EVENT_LEVELUP, analytics.PARAM_LEVEL, i) + end + end) + + dirtylarry.button("log_table", action_id, action, function() + local tbl = { + [analytics.PARAM_SCORE] = 100, + [analytics.PARAM_LEVEL] = 1, + [analytics.PARAM_CHARACTER] = "Yoda", + some_param1 = math.random(1,100), + some_param2 = true, + some_param3 = "some_string" + } + pprint("log_table()", tbl) + if analytics then + analytics.log_table(analytics.EVENT_POSTSCORE, tbl) + end + end) end diff --git a/firebase_analytics/api/firebase.script_api b/firebase_analytics/api/firebase.script_api index e6f2eee..6beaa16 100644 --- a/firebase_analytics/api/firebase.script_api +++ b/firebase_analytics/api/firebase.script_api @@ -3,13 +3,56 @@ desc: Functions and constants for interacting with Firebase Analytics members: - - name: init + - name: initialize type: function desc: Initialise analytics + - name: set_callback + type: function + desc: Sets a callback function for receiving events from the SDK. Call `firebase.analytics.set_callback(nil)` + to remove callback + parameters: + - name: callback + type: function + desc: Callback function that is executed on any event in the SDK. + + parameters: + - name: self + type: object + desc: The calling script instance + + - name: message_id + type: number + desc: "One of message types: + `firebase.analytics.MSG_ERROR` + `firebase.analytics.MSG_INSTANCE_ID`" + + - name: message + type: table + desc: A table holding the data + fields: + - name: error + type: string + optional: true + desc: The error message (if an error occurred or `nil` otherwise) + + - name: instance_id + type: string + optional: true + desc: For message_id MSG_INSTANCE_ID or `nil` otherwise. + + - name: log + type: function + desc: Log an event without parameters. + + parameters: + - name: name + type: string + desc: Event name + - name: log_string type: function - desc: Log an event with one string parameter. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/logevent) + desc: Log an event with one string parameter. parameters: - name: name @@ -24,7 +67,7 @@ - name: log_int type: function - desc: Log an event with one integer parameter. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/logevent_4) + desc: Log an event with one integer parameter. parameters: - name: name @@ -39,7 +82,7 @@ - name: log_number type: function - desc: Log an event with one float parameter. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/logevent_2) + desc: Log an event with one float parameter. parameters: - name: name @@ -54,7 +97,7 @@ - name: log_table type: function - desc: Log an event with table parameters. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/logevent_6) + desc: Log an event with table parameters. parameters: - name: name @@ -64,13 +107,9 @@ type: table desc: Table with parameters (key-value pairs) - - name: set_screen - type: function - desc: DEPRECATED. Use log_string(analytics.EVENT_SCREENVIEW, "screen name", "screen class"). - - name: set_user_id type: function - desc: Sets the user ID property. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/setuserid) + desc: Sets the user ID property. parameters: - name: user_id @@ -79,7 +118,7 @@ - name: set_user_property type: function - desc: Set a user property to the given value. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/setuserproperty) + desc: Set a user property to the given value. parameters: - name: name @@ -91,32 +130,50 @@ - name: reset type: function - desc: Clears all data for this app from the device and resets the app instance id. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/resetata) + desc: Clears all data for this app from the device and resets the app instance id. - name: get_id type: function - desc: Get the instance ID from the service. (Official docs https://firebase.google.com/docs/reference/cpp/namespace/firebase/getnstanceid) + desc: Get the instance ID from the service. Returned in callback with MSG_INSTANCE_ID message_id. + + - name: set_enabled + type: function + desc: Sets whether analytics collection is enabled for this app on this device. parameters: - - name: callback - type: function - desc: Function to invoke with the id (self, id) + - name: key + type: boolean + desc: The value + +#***** EVENTS ***************************************************************************************** + + - name: MSG_ERROR + type: number + desc: Event generated when an error occurred. + + - name: MSG_INSTANCE_ID + type: number + desc: Event generated when instance_id ready after `firebase.analytics.get_id()` call #***** EVENTS ***************************************************************************************** + - name: EVENT_ADIMPRESSION + type: string + desc: Predefined event + - name: EVENT_ADDPAYMENTINFO type: string desc: Predefined event - - name: EVENT_ADDTOCART + - name: EVENT_ADDSHIPPINGINFO type: string desc: Predefined event - - name: EVENT_ADDTOWISHLIST + - name: EVENT_ADDTOCART type: string desc: Predefined event - - name: EVENT_ADIMPRESSION + - name: EVENT_ADDTOWISHLIST type: string desc: Predefined event @@ -132,18 +189,10 @@ type: string desc: Predefined event - - name: EVENT_CHECKOUTPROGRESS - type: string - desc: Predefined event - - name: EVENT_EARNVIRTUALCURRENCY type: string desc: Predefined event - - name: EVENT_ECOMMERCEPURCHASE - type: string - desc: Predefined event - - name: EVENT_GENERATELEAD type: string desc: Predefined event @@ -172,11 +221,11 @@ type: string desc: Predefined event - - name: EVENT_PRESENTOFFER + - name: EVENT_PURCHASE type: string desc: Predefined event - - name: EVENT_PURCHASEREFUND + - name: EVENT_REFUND type: string desc: Predefined event @@ -196,7 +245,11 @@ type: string desc: Predefined event - - name: EVENT_SETCHECKOUTOPTION + - name: EVENT_SELECTITEM + type: string + desc: Predefined event + + - name: EVENT_SELECTPROMOTION type: string desc: Predefined event @@ -224,52 +277,28 @@ type: string desc: Predefined event - - name: EVENT_VIEWITEM - type: string - desc: Predefined event - - - name: EVENT_VIEWITEMLIST - type: string - desc: Predefined event - - - name: EVENT_VIEWSEARCHRESULTS - type: string - desc: Predefined event - - - name: EVENT_ADDSHIPPINGINFO - type: string - desc: Predefined event - - - name: EVENT_PURCHASE - type: string - desc: Predefined event - - - name: EVENT_REFUND + - name: EVENT_VIEWCART type: string desc: Predefined event - - name: EVENT_SELECTITEM + - name: EVENT_VIEWITEM type: string desc: Predefined event - - name: EVENT_SELECTPROMOTION + - name: EVENT_VIEWITEMLIST type: string desc: Predefined event - - name: EVENT_VIEWCART + - name: EVENT_VIEWPROMOTION type: string desc: Predefined event - - name: EVENT_VIEWPROMOTION + - name: EVENT_VIEWSEARCHRESULTS type: string desc: Predefined event #***** PARAMETERS ***************************************************************************************** - - name: PARAM_ACHIEVEMENTID - type: string - desc: Predefined parameter - - name: PARAM_ADFORMAT type: string desc: Predefined parameter @@ -294,19 +323,19 @@ type: string desc: Predefined parameter - - name: PARAM_CAMPAIGN + - name: PARAM_CP1 type: string desc: Predefined parameter - - name: PARAM_CHARACTER + - name: PARAM_CAMPAIGN type: string desc: Predefined parameter - - name: PARAM_CHECKOUTSTEP + - name: PARAM_CAMPAIGNID type: string desc: Predefined parameter - - name: PARAM_CHECKOUTOPTION + - name: PARAM_CHARACTER type: string desc: Predefined parameter @@ -322,7 +351,7 @@ type: string desc: Predefined parameter - - name: PARAM_CP1 + - name: PARAM_CREATIVEFORMAT type: string desc: Predefined parameter @@ -342,10 +371,18 @@ type: string desc: Predefined parameter + - name: PARAM_DISCOUNT + type: string + desc: Predefined parameter + - name: PARAM_ENDDATE type: string desc: Predefined parameter + - name: PARAM_EXTENDSESSION + type: string + desc: Predefined parameter + - name: PARAM_FLIGHTNUMBER type: string desc: Predefined parameter @@ -366,182 +403,181 @@ type: string desc: Predefined parameter - - name: PARAM_ITEMID + - name: PARAM_ITEMCATEGORY2 type: string desc: Predefined parameter - - name: PARAM_ITEMLOCATIONID + - name: PARAM_ITEMCATEGORY3 type: string desc: Predefined parameter - - name: PARAM_ITEMNAME + - name: PARAM_ITEMCATEGORY4 type: string desc: Predefined parameter - - name: PARAM_ITEMLIST + - name: PARAM_ITEMCATEGORY5 type: string desc: Predefined parameter - - name: PARAM_ITEMVARIANT + - name: PARAM_ITEMID type: string desc: Predefined parameter - - name: PARAM_LEVEL + - name: PARAM_ITEMLISTID type: string desc: Predefined parameter - - name: PARAM_LOCATION + - name: PARAM_ITEMLISTNAME type: string desc: Predefined parameter - - name: PARAM_MEDIUM + - name: PARAM_ITEMNAME type: string desc: Predefined parameter - - name: PARAM_NUMBEROFNIGHTS + - name: PARAM_ITEMVARIANT type: string desc: Predefined parameter - - name: PARAM_NUMBEROFPASSENGERS + - name: PARAM_ITEMS type: string desc: Predefined parameter - - name: PARAM_NUMBEROFROOMS + - name: PARAM_LEVEL type: string desc: Predefined parameter - - name: PARAM_ORIGIN + - name: PARAM_LEVELNAME type: string desc: Predefined parameter - - name: PARAM_PRICE + - name: PARAM_LOCATION type: string desc: Predefined parameter - - name: PARAM_QUANTITY + - name: PARAM_LOCATIONID type: string desc: Predefined parameter - - name: PARAM_SCORE + - name: PARAM_MARKETINGTACTIC type: string desc: Predefined parameter - - name: PARAM_SCREENCLASS + - name: PARAM_MEDIUM type: string desc: Predefined parameter - - name: PARAM_SCREENNAME + - name: PARAM_METHOD type: string desc: Predefined parameter - - name: PARAM_SEARCHTERM + - name: PARAM_NUMBEROFNIGHTS type: string desc: Predefined parameter - - name: PARAM_SHIPPING + - name: PARAM_NUMBEROFPASSENGERS type: string desc: Predefined parameter - - name: PARAM_SIGNUPMETHOD + - name: PARAM_NUMBEROFROOMS type: string desc: Predefined parameter - - name: PARAM_METHOD + - name: PARAM_ORIGIN type: string desc: Predefined parameter - - name: PARAM_SOURCE + - name: PARAM_PAYMENTTYPE type: string desc: Predefined parameter - - name: PARAM_STARTDATE + - name: PARAM_PRICE type: string desc: Predefined parameter - - name: PARAM_TAX + - name: PARAM_PROMOTIONID type: string desc: Predefined parameter - - name: PARAM_TERM + - name: PARAM_PROMOTIONNAME type: string desc: Predefined parameter - - name: PARAM_TRANSACTIONID + - name: PARAM_QUANTITY type: string desc: Predefined parameter - - name: PARAM_TRAVELCLASS + - name: PARAM_SCORE type: string desc: Predefined parameter - - name: PARAM_VALUE + - name: PARAM_SCREENCLASS type: string desc: Predefined parameter - - name: PARAM_VIRTUALCURRENCYNAME + - name: PARAM_SCREENNAME type: string desc: Predefined parameter - - name: PARAM_LEVELNAME + - name: PARAM_SEARCHTERM type: string desc: Predefined parameter - - name: PARAM_SUCCESS + - name: PARAM_SHIPPING type: string desc: Predefined parameter - - name: PARAM_EXTENDSESSION + - name: PARAM_SHIPPINGTIER type: string desc: Predefined parameter - - name: PARAM_DISCOUNT + - name: PARAM_SOURCE type: string desc: Predefined parameter - - name: PARAM_ITEMCATEGORY2 + - name: PARAM_SOURCEPLATFORM type: string desc: Predefined parameter - - name: PARAM_ITEMCATEGORY3 + - name: PARAM_STARTDATE type: string desc: Predefined parameter - - name: PARAM_ITEMCATEGORY4 + - name: PARAM_SUCCESS type: string desc: Predefined parameter - - name: PARAM_ITEMCATEGORY5 + - name: PARAM_TAX type: string desc: Predefined parameter - - name: PARAM_ITEMLISTID + - name: PARAM_TERM type: string desc: Predefined parameter - - name: PARAM_ITEMLISTNAME + - name: PARAM_TRANSACTIONID type: string desc: Predefined parameter - - name: PARAM_ITEMS + - name: PARAM_TRAVELCLASS type: string desc: Predefined parameter - - name: PARAM_LOCATIONID + - name: PARAM_VALUE type: string desc: Predefined parameter - - name: PARAM_PAYMENTTYPE + - name: PARAM_VIRTUALCURRENCYNAME type: string desc: Predefined parameter - - name: PARAM_PROMOTIONID - type: string - desc: Predefined parameter +#***** PROPERTIES ***************************************************************************************** - - name: PARAM_PROMOTIONNAME + - name: PROP_ALLOWADPERSONALIZATIONSIGNALS type: string - desc: Predefined parameter + desc: Predefined property - - name: PARAM_SHIPPINGTIER + - name: PROP_SIGNUPMETHOD type: string - desc: Predefined parameter + desc: Predefined property + diff --git a/firebase_analytics/ext.manifest b/firebase_analytics/ext.manifest index 8c7a3b8..6255905 100644 --- a/firebase_analytics/ext.manifest +++ b/firebase_analytics/ext.manifest @@ -1,26 +1,9 @@ -name: "FirebaseAnalytics" +name: "FirebaseAnalyticsExt" platforms: android: context: aaptExtraPackages: ['com.google.android.gms.common'] - - arm64-android: - context: - flags: ["-std=c++11"] - - armv7-android: - context: - flags: ["-std=c++11"] - - armv7-ios: - context: - flags: ["-std=c++11"] - - arm64-ios: - context: - flags: ["-std=c++11"] - - x86_64-ios: - context: - flags: ["-std=c++11"] + ios: + context: + linkFlags: ["-ObjC"] diff --git a/firebase_analytics/include/firebase/analytics/event_names.h b/firebase_analytics/include/firebase/analytics/event_names.h new file mode 100644 index 0000000..4f1d4e5 --- /dev/null +++ b/firebase_analytics/include/firebase/analytics/event_names.h @@ -0,0 +1,472 @@ +// Copyright 2023 Google Inc. All Rights Reserved. + +#ifndef FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_EVENT_NAMES_H_ +#define FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_EVENT_NAMES_H_ + +/// @brief Namespace that encompasses all Firebase APIs. +namespace firebase { +/// @brief Firebase Analytics API. +namespace analytics { + + + +/// @defgroup event_names Analytics Events +/// +/// Predefined event names. +/// +/// An Event is an important occurrence in your app that you want to +/// measure. You can report up to 500 different types of Events per app +/// and you can associate up to 25 unique parameters with each Event type. +/// Some common events are suggested below, but you may also choose to +/// specify custom Event types that are associated with your specific app. +/// Each event type is identified by a unique name. Event names can be up +/// to 40 characters long, may only contain alphanumeric characters and +/// underscores ("_"), and must start with an alphabetic character. The +/// "firebase_", "google_", and "ga_" prefixes are reserved and should not +/// be used. +/// @{ + + +/// Ad Impression event. This event signifies when a user sees an ad +/// impression. Note: If you supply the @c AnalyticsParameterValue +/// parameter, you must also supply the @c AnalyticsParameterCurrency +/// parameter so that revenue metrics can be computed accurately. Params: +/// +/// +static const char*const kEventAdImpression = + "ad_impression"; + +/// Add Payment Info event. This event signifies that a user has submitted +/// their payment information. Note: If you supply the @c +/// AnalyticsParameterValue parameter, you must also supply the @c +/// AnalyticsParameterCurrency parameter so that revenue metrics can be +/// computed accurately. Params: +/// +/// +static const char*const kEventAddPaymentInfo = + "add_payment_info"; + +/// Add Shipping Info event. This event signifies that a user has +/// submitted their shipping information. Note: If you supply the @c +/// AnalyticsParameterValue parameter, you must also supply the @c +/// AnalyticsParameterCurrency parameter so that revenue metrics can be +/// computed accurately. Params: +/// +/// +static const char*const kEventAddShippingInfo = + "add_shipping_info"; + +/// E-Commerce Add To Cart event. This event signifies that an item(s) was +/// added to a cart for purchase. Add this event to a funnel with @c +/// AnalyticsEventPurchase to gauge the effectiveness of your +/// checParameter(kout, If you supply the @c AnalyticsParameterValue +/// parameter), you must also supply the @c AnalyticsParameterCurrency +/// parameter so that revenue metrics can be computed accurately. Params: +/// +/// +static const char*const kEventAddToCart = "add_to_cart"; + +/// E-Commerce Add To Wishlist event. This event signifies that an item +/// was added to a wishlist. Use this event to identify popular gift +/// items. Note: If you supply the @c AnalyticsParameterValue parameter, +/// you must also supply the @c AnalyticsParameterCurrency parameter so +/// that revenue metrics can be computed accurately. Params: +/// +/// +static const char*const kEventAddToWishlist = + "add_to_wishlist"; + +/// App Open event. By logging this event when an App becomes active, +/// developers can understand how often users leave and return during the +/// course of a Session. Although Sessions are automatically reported, +/// this event can provide further clarification around the continuous +/// engagement of app-users. +static const char*const kEventAppOpen = "app_open"; + +/// E-Commerce Begin Checkout event. This event signifies that a user has +/// begun the process of checking out. Add this event to a funnel with +/// your @c AnalyticsEventPurchase event to gauge the effectiveness of +/// your checkout process. Note: If you supply the @c +/// AnalyticsParameterValue parameter, you must also supply the @c +/// AnalyticsParameterCurrency parameter so that revenue metrics can be +/// computed accurately. Params: +/// +/// +static const char*const kEventBeginCheckout = + "begin_checkout"; + +/// Campaign Detail event. Log this event to supply the referral details +/// of a re-engagement campaign. Note: you must supply at least one of the +/// required parameters AnalyticsParameterSource, AnalyticsParameterMedium +/// or AnalyticsParameterCampaign. Params: +/// +/// +static const char*const kEventCampaignDetails = + "campaign_details"; + +/// Earn Virtual Currency event. This event tracks the awarding of virtual +/// currency in your app. Log this along with @c +/// AnalyticsEventSpendVirtualCurrency to better understand your virtual +/// economy. Params: +/// +/// +static const char*const kEventEarnVirtualCurrency + = "earn_virtual_currency"; + +/// Generate Lead event. Log this event when a lead has been generated in +/// the app to understand the efficacy of your install and re-engagement +/// campaigns. Note: If you supply the @c AnalyticsParameterValue +/// parameter, you must also supply the @c AnalyticsParameterCurrency +/// parameter so that revenue metrics can be computed accurately. Params: +/// +/// +static const char*const kEventGenerateLead = + "generate_lead"; + +/// Join Group event. Log this event when a user joins a group such as a +/// guild, team or family. Use this event to analyze how popular certain +/// groups or social features are in your app. Params: +/// +/// +static const char*const kEventJoinGroup = "join_group"; + +/// Level End event. Log this event when the user finishes a level. +/// Params: +/// +/// +static const char*const kEventLevelEnd = "level_end"; + +/// Level Start event. Log this event when the user starts a new level. +/// Params: +/// +/// +static const char*const kEventLevelStart = "level_start"; + +/// Level Up event. This event signifies that a player has leveled up in +/// your gaming app. It can help you gauge the level distribution of your +/// userbase and help you identify certain levels that are difficult to +/// pass. Params: +/// +/// +static const char*const kEventLevelUp = "level_up"; + +/// Login event. Apps with a login feature can report this event to +/// signify that a user has logged in. +static const char*const kEventLogin = "login"; + +/// Post Score event. Log this event when the user posts a score in your +/// gaming app. This event can help you understand how users are actually +/// performing in your game and it can help you correlate high scores with +/// certain audiences or behaviors. Params: +/// +/// +static const char*const kEventPostScore = "post_score"; + +/// E-Commerce Purchase event. This event signifies that an item(s) was +/// purchased by a user. Note: This is different from the in-app purchase +/// event, which is reported automatically for App Store-based apps. Note: +/// If you supply the @c AnalyticsParameterValue parameter, you must also +/// supply the @c AnalyticsParameterCurrency parameter so that revenue +/// metrics can be computed accurately. Params: +/// +/// +static const char*const kEventPurchase = "purchase"; + +/// E-Commerce Refund event. This event signifies that a refund was +/// issued. Note: If you supply the @c AnalyticsParameterValue parameter, +/// you must also supply the @c AnalyticsParameterCurrency parameter so +/// that revenue metrics can be computed accurately. Params: +/// +/// +static const char*const kEventRefund = "refund"; + +/// E-Commerce Remove from Cart event. This event signifies that an +/// item(s) was removed from a cart. Note: If you supply the @c +/// AnalyticsParameterValue parameter, you must also supply the @c +/// AnalyticsParameterCurrency parameter so that revenue metrics can be +/// computed accurately. Params: +/// +/// +static const char*const kEventRemoveFromCart = + "remove_from_cart"; + +/// Screen View event. This event signifies a screen view. Use this when a +/// screen transition occurs. This event can be logged irrespective of +/// whether automatic screen tracking is enabled. Params: +/// +/// +static const char*const kEventScreenView = "screen_view"; + +/// Search event. Apps that support search features can use this event to +/// contextualize search operations by supplying the appropriate, +/// corresponding parameters. This event can help you identify the most +/// popular content in your app. Params: +/// +/// +static const char*const kEventSearch = "search"; + +/// Select Content event. This general purpose event signifies that a user +/// has selected some content of a certain type in an app. The content can +/// be any object in your app. This event can help you identify popular +/// content and categories of content in your app. Params: +/// +/// +static const char*const kEventSelectContent = + "select_content"; + +/// Select Item event. This event signifies that an item was selected by a +/// user from a list. Use the appropriate parameters to contextualize the +/// event. Use this event to discover the most popular items selected. +/// Params: +/// +/// +static const char*const kEventSelectItem = "select_item"; + +/// Select promotion event. This event signifies that a user has selected +/// a promotion offer. Use the appropriate parameters to contextualize the +/// event, such as the item(s) for which the promotion applies. Params: +/// +/// +static const char*const kEventSelectPromotion = + "select_promotion"; + +/// Share event. Apps with social features can log the Share event to +/// identify the most viral content. Params: +/// +/// +static const char*const kEventShare = "share"; + +/// Sign Up event. This event indicates that a user has signed up for an +/// account in your app. The parameter signifies the method by which the +/// user signed up. Use this event to understand the different behaviors +/// between logged in and logged out users. Params: +/// +/// +static const char*const kEventSignUp = "sign_up"; + +/// Spend Virtual Currency event. This event tracks the sale of virtual +/// goods in your app and can help you identify which virtual goods are +/// the most popular objects of purchase. Params: +/// +/// +static const char*const kEventSpendVirtualCurrency + = "spend_virtual_currency"; + +/// Tutorial Begin event. This event signifies the start of the +/// on-boarding process in your app. Use this in a funnel with @c +/// AnalyticsEventTutorialComplete to understand how many users complete +/// this process and move on to the full app experience. +static const char*const kEventTutorialBegin = + "tutorial_begin"; + +/// Tutorial End event. Use this event to signify the user's completion of +/// your app's on-boarding process. Add this to a funnel with @c +/// AnalyticsEventTutorialBegin to gauge the completion rate of your +/// on-boarding process. +static const char*const kEventTutorialComplete = + "tutorial_complete"; + +/// Unlock Achievement event. Log this event when the user has unlocked an +/// achievement in your game. Since achievements generally represent the +/// breadth of a gaming experience, this event can help you understand how +/// many users are experiencing all that your game has to offer. Params: +/// +/// +static const char*const kEventUnlockAchievement = + "unlock_achievement"; + +/// E-commerce View Cart event. This event signifies that a user has +/// viewed their cart. Use this to analyze your purchase funnel. Note: If +/// you supply the @c AnalyticsParameterValue parameter, you must also +/// supply the @c AnalyticsParameterCurrency parameter so that revenue +/// metrics can be computed accurately. Params: +/// +/// +static const char*const kEventViewCart = "view_cart"; + +/// View Item event. This event signifies that a user has viewed an item. +/// Use the appropriate parameters to contextualize the event. Use this +/// event to discover the most popular items viewed in your app. Note: If +/// you supply the @c AnalyticsParameterValue parameter, you must also +/// supply the @c AnalyticsParameterCurrency parameter so that revenue +/// metrics can be computed accurately. Params: +/// +/// +static const char*const kEventViewItem = "view_item"; + +/// View Item List event. Log this event when a user sees a list of items +/// or offerings. Params: +/// +/// +static const char*const kEventViewItemList = + "view_item_list"; + +/// View Promotion event. This event signifies that a promotion was shown +/// to a user. Add this event to a funnel with the @c +/// AnalyticsEventAddToCart and @c AnalyticsEventPurchase to gauge your +/// conversion process. Params: +/// +/// +static const char*const kEventViewPromotion = + "view_promotion"; + +/// View Search Results event. Log this event when the user has been +/// presented with the results of a search. Params: +/// +/// +static const char*const kEventViewSearchResults = + "view_search_results"; +/// @} + +} // namespace analytics +} // namespace firebase + +#endif // FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_EVENT_NAMES_H_ diff --git a/firebase_analytics/include/firebase/analytics/parameter_names.h b/firebase_analytics/include/firebase/analytics/parameter_names.h new file mode 100644 index 0000000..fcc43c2 --- /dev/null +++ b/firebase_analytics/include/firebase/analytics/parameter_names.h @@ -0,0 +1,755 @@ +// Copyright 2023 Google Inc. All Rights Reserved. + +#ifndef FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_PARAMETER_NAMES_H_ +#define FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_PARAMETER_NAMES_H_ + +/// @brief Namespace that encompasses all Firebase APIs. +namespace firebase { +/// @brief Firebase Analytics API. +namespace analytics { + + + +/// @defgroup parameter_names Analytics Parameters +/// +/// Predefined event parameter names. +/// +/// Params supply information that contextualize Events. You can associate +/// up to 25 unique Params with each Event type. Some Params are suggested +/// below for certain common Events, but you are not limited to these. You +/// may supply extra Params for suggested Events or custom Params for +/// Custom events. Param names can be up to 40 characters long, may only +/// contain alphanumeric characters and underscores ("_"), and must start +/// with an alphabetic character. Param values can be up to 100 characters +/// long. The "firebase_", "google_", and "ga_" prefixes are reserved and +/// should not be used. +/// @{ + + +/// Game achievement ID (String). +/// @code +/// let params = [ +/// AnalyticsParameterAchievementID : "10_matches_won", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAchievementID = + "achievement_id"; + +/// The ad format (e.g. Banner, Interstitial, Rewarded, Native, Rewarded +/// Interstitial, Instream). (String). +/// @code +/// let params = [ +/// AnalyticsParameterAdFormat : "Banner", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAdFormat = + "ad_format"; + +/// Ad Network Click ID (String). Used for network-specific click IDs +/// which vary in format. +/// @code +/// let params = [ +/// AnalyticsParameterAdNetworParameter(kClickID, "1234567"), +/// // ... +/// ] +/// @endcode +static const char*const kParameterAdNetworkClickID + = "aclid"; + +/// The ad platform (e.g. MoPub, IronSource) (String). +/// @code +/// let params = [ +/// AnalyticsParameterAdPlatform : "MoPub", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAdPlatform = + "ad_platform"; + +/// The ad source (e.g. AdColony) (String). +/// @code +/// let params = [ +/// AnalyticsParameterAdSource : "AdColony", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAdSource = + "ad_source"; + +/// The ad unit name (e.g. Banner_03) (String). +/// @code +/// let params = [ +/// AnalyticsParameterAdUnitName : "Banner_03", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAdUnitName = + "ad_unit_name"; + +/// A product affiliation to designate a supplying company or brick and +/// mortar store location +/// (String). @code +/// let params = [ +/// AnalyticsParameterAffiliation : "Google Store", +/// // ... +/// ] +/// @endcode +static const char*const kParameterAffiliation = + "affiliation"; + +/// Campaign custom parameter (String). Used as a method of capturing +/// custom data in a campaign. Use varies by network. +/// @code +/// let params = [ +/// AnalyticsParameterCP1 : "custom_data", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCP1 = "cp1"; + +/// The individual campaign name, slogan, promo code, etc. Some networks +/// have pre-defined macro to capture campaign information, otherwise can +/// be populated by developer. Highly Recommended (String). +/// @code +/// let params = [ +/// AnalyticsParameterCampaign : "winter_promotion", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCampaign = + "campaign"; + +/// Campaign ID (String). Used for keyword analysis to identify a specific +/// product promotion or strategic campaign. This is a required key for +/// GA4 data import. +/// @code +/// let params = [ +/// AnalyticsParameterCampaignID : "7877652710", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCampaignID = + "campaign_id"; + +/// Character used in game (String). +/// @code +/// let params = [ +/// AnalyticsParameterCharacter : "beat_boss", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCharacter = + "character"; + +/// Campaign content (String). +static const char*const kParameterContent = "content"; + +/// Type of content selected (String). +/// @code +/// let params = [ +/// AnalyticsParameterContentType : "news article", +/// // ... +/// ] +/// @endcode +static const char*const kParameterContentType = + "content_type"; + +/// Coupon code used for a purchase (String). +/// @code +/// let params = [ +/// AnalyticsParameterCoupon : "SUMMER_FUN", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCoupon = "coupon"; + +/// Creative Format (String). Used to identify the high-level +/// classification of the type of ad served by a specific campaign. +/// @code +/// let params = [ +/// AnalyticsParameterCreativeFormat : "display", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCreativeFormat = + "creative_format"; + +/// The name of a creative used in a promotional spot (String). +/// @code +/// let params = [ +/// AnalyticsParameterCreativeName : "Summer Sale", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCreativeName = + "creative_name"; + +/// The name of a creative slot (String). +/// @code +/// let params = [ +/// AnalyticsParameterCreativeSlot : "summer_banner2", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCreativeSlot = + "creative_slot"; + +/// Currency of the purchase or items associated with the event, in +/// 3-letter +/// ISO_4217 format (String). +/// @code +/// let params = [ +/// AnalyticsParameterCurrency : "USD", +/// // ... +/// ] +/// @endcode +static const char*const kParameterCurrency = + "currency"; + +/// Flight or Travel destination (String). +/// @code +/// let params = [ +/// AnalyticsParameterDestination : "Mountain View, CA", +/// // ... +/// ] +/// @endcode +static const char*const kParameterDestination = + "destination"; + +/// Monetary value of discount associated with a purchase (Double). +/// @code +/// let params = [ +/// AnalyticsParameterDiscount : 2.0, +/// AnalyticsParameterCurrency : "USD", // e.g. $2.00 USD +/// // ... +/// ] +/// @endcode +static const char*const kParameterDiscount = + "discount"; + +/// The arrival date, check-out date or rental end date for the item. This +/// should be in YYYY-MM-DD format (String). +/// @code +/// let params = [ +/// AnalyticsParameterEndDate : "2015-09-14", +/// // ... +/// ] +/// @endcode +static const char*const kParameterEndDate = "end_date"; + +/// Indicates that the associated event should either extend the current +/// session or start a new session if no session was active when the event +/// was logged. Specify 1 to extend the current session or to start a new +/// session; any other value will not extend or start a session. +/// @code +/// let params = [ +/// AnalyticsParameterExtendSession : 1, +/// // ... +/// ] +/// @endcode +static const char*const kParameterExtendSession = + "extend_session"; + +/// Flight number for travel events (String). +/// @code +/// let params = [ +/// AnalyticsParameterFlightNumber : "ZZ800", +/// // ... +/// ] +/// @endcode +static const char*const kParameterFlightNumber = + "flight_number"; + +/// Group/clan/guild ID (String). +/// @code +/// let params = [ +/// AnalyticsParameterGroupID : "g1", +/// // ... +/// ] +/// @endcode +static const char*const kParameterGroupID = "group_id"; + +/// The index of the item in a list (Int). +/// @code +/// let params = [ +/// AnalyticsParameterIndex : 5, +/// // ... +/// ] +/// @endcode +static const char*const kParameterIndex = "index"; + +/// Item brand (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemBrand : "Google", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemBrand = + "item_brand"; + +/// Item category (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemCategory : "pants", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemCategory = + "item_category"; + +/// Item Category (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemCategory2 : "pants", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemCategory2 = + "item_category2"; + +/// Item Category (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemCategory3 : "pants", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemCategory3 = + "item_category3"; + +/// Item Category (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemCategory4 : "pants", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemCategory4 = + "item_category4"; + +/// Item Category (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemCategory5 : "pants", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemCategory5 = + "item_category5"; + +/// Item ID (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemID : "SKU_12345", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemID = "item_id"; + +/// The ID of the list in which the item was presented to the +/// user (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemListID : "ABC123", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemListID = + "item_list_id"; + +/// The name of the list in which the item was presented to the user +/// (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemListName : "Related products", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemListName = + "item_list_name"; + +/// Item Name (context-specific) (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemName : "jeggings", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemName = + "item_name"; + +/// Item variant (String). +/// @code +/// let params = [ +/// AnalyticsParameterItemVariant : "Black", +/// // ... +/// ] +/// @endcode +static const char*const kParameterItemVariant = + "item_variant"; + +/// The list of items involved in the transaction expressed as `[[String: +/// Any]]`. +/// @code +/// let params = [ +/// AnalyticsParameterItems : [ +/// [AnalyticsParameterItemName : "jeggings", AnalyticsParameterItemCategory : "pants"], +/// [AnalyticsParameterItemName : "boots", AnalyticsParameterItemCategory : "shoes"], +/// ], +/// ] +/// @endcode +static const char*const kParameterItems = "items"; + +/// Level in game (Int). +/// @code +/// let params = [ +/// AnalyticsParameterLevel : 42, +/// // ... +/// ] +/// @endcode +static const char*const kParameterLevel = "level"; + +/// The name of a level in a game (String). +/// @code +/// let params = [ +/// AnalyticsParameterLevelName : "room_1", +/// // ... +/// ] +/// @endcode +static const char*const kParameterLevelName = + "level_name"; + +/// Location (String). The Google Place ID +/// that corresponds to the associated event. Alternatively, you can supply your own custom +/// Location ID. +/// @code +/// let params = [ +/// AnalyticsParameterLocation : "ChIJiyj437sx3YAR9kUWC8QkLzQ", +/// // ... +/// ] +/// @endcode +static const char*const kParameterLocation = + "location"; + +/// The location associated with the event. Preferred to be the Google +/// Place ID that corresponds to the +/// associated item but could be overridden to a custom location ID +/// string.(String). +/// @code +/// let params = [ +/// AnalyticsParameterLocationID : "ChIJiyj437sx3YAR9kUWC8QkLzQ", +/// // ... +/// ] +/// @endcode +static const char*const kParameterLocationID = + "location_id"; + +/// Marketing Tactic (String). Used to identify the targeting criteria +/// applied to a specific campaign. +/// @code +/// let params = [ +/// AnalyticsParameterMarParameter(ketingTactic, "Remarketing"), +/// // ... +/// ] +/// @endcode +static const char*const kParameterMarketingTactic + = "marketing_tactic"; + +/// The advertising or marParameter(keting, cpc, banner, email), push. +/// Highly recommended (String). +/// @code +/// let params = [ +/// AnalyticsParameterMedium : "email", +/// // ... +/// ] +/// @endcode +static const char*const kParameterMedium = "medium"; + +/// A particular approach used in an operation; for example, "facebook" or +/// "email" in the context of a sign_up or login event. (String). +/// @code +/// let params = [ +/// AnalyticsParameterMethod : "google", +/// // ... +/// ] +/// @endcode +static const char*const kParameterMethod = "method"; + +/// Number of nights staying at hotel (Int). +/// @code +/// let params = [ +/// AnalyticsParameterNumberOfNights : 3, +/// // ... +/// ] +/// @endcode +static const char*const kParameterNumberOfNights + = "number_of_nights"; + +/// Number of passengers traveling (Int). +/// @code +/// let params = [ +/// AnalyticsParameterNumberOfPassengers : 11, +/// // ... +/// ] +/// @endcode +static const char*const kParameterNumberOfPassengers + = "number_of_passengers"; + +/// Number of rooms for travel events (Int). +/// @code +/// let params = [ +/// AnalyticsParameterNumberOfRooms : 2, +/// // ... +/// ] +/// @endcode +static const char*const kParameterNumberOfRooms = + "number_of_rooms"; + +/// Flight or Travel origin (String). +/// @code +/// let params = [ +/// AnalyticsParameterOrigin : "Mountain View, CA", +/// // ... +/// ] +/// @endcode +static const char*const kParameterOrigin = "origin"; + +/// The chosen method of payment (String). +/// @code +/// let params = [ +/// AnalyticsParameterPaymentType : "Visa", +/// // ... +/// ] +/// @endcode +static const char*const kParameterPaymentType = + "payment_type"; + +/// Purchase price (Double). +/// @code +/// let params = [ +/// AnalyticsParameterPrice : 1.0, +/// AnalyticsParameterCurrency : "USD", // e.g. $1.00 USD +/// // ... +/// ] +/// @endcode +static const char*const kParameterPrice = "price"; + +/// The ID of a product promotion (String). +/// @code +/// let params = [ +/// AnalyticsParameterPromotionID : "ABC123", +/// // ... +/// ] +/// @endcode +static const char*const kParameterPromotionID = + "promotion_id"; + +/// The name of a product promotion (String). +/// @code +/// let params = [ +/// AnalyticsParameterPromotionName : "Summer Sale", +/// // ... +/// ] +/// @endcode +static const char*const kParameterPromotionName = + "promotion_name"; + +/// Purchase quantity (Int). +/// @code +/// let params = [ +/// AnalyticsParameterQuantity : 1, +/// // ... +/// ] +/// @endcode +static const char*const kParameterQuantity = + "quantity"; + +/// Score in game (Int). +/// @code +/// let params = [ +/// AnalyticsParameterScore : 4200, +/// // ... +/// ] +/// @endcode +static const char*const kParameterScore = "score"; + +/// Current screen class, such as the class name of the UIViewController, +/// logged with screen_view event and added to every event (String). +/// @code +/// let params = [ +/// AnalyticsParameterScreenClass : "LoginViewController", +/// // ... +/// ] +/// @endcode +static const char*const kParameterScreenClass = + "screen_class"; + +/// Current screen name, such as the name of the UIViewController, logged +/// with screen_view event and added to every event (String). +/// @code +/// let params = [ +/// AnalyticsParameterScreenName : "LoginView", +/// // ... +/// ] +/// @endcode +static const char*const kParameterScreenName = + "screen_name"; + +/// The search string/keywords used (String). +/// @code +/// let params = [ +/// AnalyticsParameterSearchTerm : "periodic table", +/// // ... +/// ] +/// @endcode +static const char*const kParameterSearchTerm = + "search_term"; + +/// Shipping cost associated with a transaction (Double). +/// @code +/// let params = [ +/// AnalyticsParameterShipping : 5.99, +/// AnalyticsParameterCurrency : "USD", // e.g. $5.99 USD +/// // ... +/// ] +/// @endcode +static const char*const kParameterShipping = + "shipping"; + +/// The shipping tier (e.g. Ground, Air, Next-day) selected for delivery +/// of the purchased item (String). +/// @code +/// let params = [ +/// AnalyticsParameterShippingTier : "Ground", +/// // ... +/// ] +/// @endcode +static const char*const kParameterShippingTier = + "shipping_tier"; + +/// The origin of your traffic, such as an Ad network (for example, +/// google) or partner (urban airship). Identify the advertiser, site, +/// publication, etc. that is sending traffic to your property. Highly +/// recommended (String). +/// @code +/// let params = [ +/// AnalyticsParameterSource : "InMobi", +/// // ... +/// ] +/// @endcode +static const char*const kParameterSource = "source"; + +/// Source Platform (String). Used to identify the platform responsible +/// for directing traffic to a given Analytics property (e.g., a buying +/// platform where budgets, targeting criteria, etc. are set, a platform +/// for managing organic traffic data, etc.). +/// @code +/// let params = [ +/// AnalyticsParameterSourcePlatform : "sa360", +/// // ... +/// ] +/// @endcode +static const char*const kParameterSourcePlatform = + "source_platform"; + +/// The departure date, check-in date or rental start date for the item. +/// This should be in YYYY-MM-DD format (String). +/// @code +/// let params = [ +/// AnalyticsParameterStartDate : "2015-09-14", +/// // ... +/// ] +/// @endcode +static const char*const kParameterStartDate = + "start_date"; + +/// The result of an operation. Specify 1 to indicate success and 0 to +/// indicate failure (Int). +/// @code +/// let params = [ +/// AnalyticsParameterSuccess : 1, +/// // ... +/// ] +/// @endcode +static const char*const kParameterSuccess = "success"; + +/// Tax cost associated with a transaction (Double). +/// @code +/// let params = [ +/// AnalyticsParameterTax : 2.43, +/// AnalyticsParameterCurrency : "USD", // e.g. $2.43 USD +/// // ... +/// ] +/// @endcode +static const char*const kParameterTax = "tax"; + +/// If you're manually tagging keyword campaigns, you should use utm_term +/// to specify the keyword (String). +/// @code +/// let params = [ +/// AnalyticsParameterTerm : "game", +/// // ... +/// ] +/// @endcode +static const char*const kParameterTerm = "term"; + +/// The unique identifier of a transaction (String). +/// @code +/// let params = [ +/// AnalyticsParameterTransactionID : "T12345", +/// // ... +/// ] +/// @endcode +static const char*const kParameterTransactionID = + "transaction_id"; + +/// Travel class (String). +/// @code +/// let params = [ +/// AnalyticsParameterTravelClass : "business", +/// // ... +/// ] +/// @endcode +static const char*const kParameterTravelClass = + "travel_class"; + +/// A context-specific numeric value which is accumulated automatically +/// for each event type. This is a general purpose parameter that is +/// useful for accumulating a key metric that pertains to an event. +/// Examples include revenue, distance, time and points. Value should be +/// specified as Int or Double. Notes: Values for pre-defined +/// currency-related events (such as @c AnalyticsEventAddToCart) should be +/// supplied using Double and must be accompanied by a @c +/// AnalyticsParameterCurrency parameter. The valid range of accumulated +/// values is [-9,223,372,036,854.77, 9,223,372,036,854.77]. Supplying a +/// non-numeric value, omitting the corresponding @c +/// AnalyticsParameterCurrency parameter, or supplying an invalid +/// currency code for conversion events will cause that +/// conversion to be omitted from reporting. +/// @code +/// let params = [ +/// AnalyticsParameterValue : 3.99, +/// AnalyticsParameterCurrency : "USD", // e.g. $3.99 USD +/// // ... +/// ] +/// @endcode +static const char*const kParameterValue = "value"; + +/// Name of virtual currency type (String). +/// @code +/// let params = [ +/// AnalyticsParameterVirtualCurrencyName : "virtual_currency_name", +/// // ... +/// ] +/// @endcode +static const char*const kParameterVirtualCurrencyName + = "virtual_currency_name"; +/// @} + +} // namespace analytics +} // namespace firebase + +#endif // FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_PARAMETER_NAMES_H_ diff --git a/firebase_analytics/include/firebase/analytics/user_property_names.h b/firebase_analytics/include/firebase/analytics/user_property_names.h new file mode 100644 index 0000000..155fea8 --- /dev/null +++ b/firebase_analytics/include/firebase/analytics/user_property_names.h @@ -0,0 +1,50 @@ +// Copyright 2023 Google Inc. All Rights Reserved. + +#ifndef FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_USER_PROPERTY_NAMES_H_ +#define FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_USER_PROPERTY_NAMES_H_ + +/// @brief Namespace that encompasses all Firebase APIs. +namespace firebase { +/// @brief Firebase Analytics API. +namespace analytics { + + + +/// @defgroup user_property_names Analytics User Properties +/// +/// Predefined user property names. +/// +/// A UserProperty is an attribute that describes the app-user. By +/// supplying UserProperties, you can later analyze different behaviors of +/// various segments of your userbase. You may supply up to 25 unique +/// UserProperties per app, and you can use the name and value of your +/// choosing for each one. UserProperty names can be up to 24 characters +/// long, may only contain alphanumeric characters and underscores ("_"), +/// and must start with an alphabetic character. UserProperty values can +/// be up to 36 characters long. The "firebase_", "google_", and "ga_" +/// prefixes are reserved and should not be used. +/// @{ + + +/// Indicates whether events logged by Google Analytics can be used to +/// personalize ads for the user. Set to "YES" to enable, or "NO" to +/// disable. Default is enabled. See the +/// documentation for +/// more details and information about related settings. +/// +/// @code +/// Analytics.setUserProperty("NO", forName: AnalyticsUserPropertyAllowAdPersonalizationSignals) +/// @endcode +static const char*const kUserPropertyAllowAdPersonalizationSignals + = "allow_personalized_ads"; + +/// The method used to sign in. For example, "google", "facebook" or +/// "twitter". +static const char*const kUserPropertySignUpMethod + = "sign_up_method"; +/// @} + +} // namespace analytics +} // namespace firebase + +#endif // FIREBASE_ANALYTICS_CLIENT_CPP_INCLUDE_FIREBASE_ANALYTICS_USER_PROPERTY_NAMES_H_ diff --git a/firebase_analytics/include/firebase_analytics.h b/firebase_analytics/include/firebase_analytics.h deleted file mode 100644 index 332d6ea..0000000 --- a/firebase_analytics/include/firebase_analytics.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include "firebase/analytics.h" - -void FirebaseAnalytics_Safe_LogEvent(lua_State*, const char* name, const firebase::analytics::Parameter* params, size_t number_of_parameters); diff --git a/firebase_analytics/lib/arm64-android/libfirebase_analytics.a b/firebase_analytics/lib/arm64-android/libfirebase_analytics.a deleted file mode 100644 index bf7678b..0000000 Binary files a/firebase_analytics/lib/arm64-android/libfirebase_analytics.a and /dev/null differ diff --git a/firebase_analytics/lib/arm64-ios/libfirebase_analytics.a b/firebase_analytics/lib/arm64-ios/libfirebase_analytics.a deleted file mode 100644 index 5ca4523..0000000 Binary files a/firebase_analytics/lib/arm64-ios/libfirebase_analytics.a and /dev/null differ diff --git a/firebase_analytics/lib/armv7-android/libfirebase_analytics.a b/firebase_analytics/lib/armv7-android/libfirebase_analytics.a deleted file mode 100644 index 66312be..0000000 Binary files a/firebase_analytics/lib/armv7-android/libfirebase_analytics.a and /dev/null differ diff --git a/firebase_analytics/lib/x86_64-ios/libfirebase_analytics.a b/firebase_analytics/lib/x86_64-ios/libfirebase_analytics.a deleted file mode 100644 index 0204425..0000000 Binary files a/firebase_analytics/lib/x86_64-ios/libfirebase_analytics.a and /dev/null differ diff --git a/firebase_analytics/manifests/android/build.gradle b/firebase_analytics/manifests/android/build.gradle index 2ab21cc..28b1eea 100644 --- a/firebase_analytics/manifests/android/build.gradle +++ b/firebase_analytics/manifests/android/build.gradle @@ -3,6 +3,6 @@ repositories { } dependencies { - implementation 'com.google.firebase:firebase-analytics:18.0.3' - implementation 'com.google.android.gms:play-services-base:17.6.0' + implementation 'com.google.firebase:firebase-analytics:21.5.0' + implementation 'com.google.android.gms:play-services-base:18.2.0' } diff --git a/firebase_analytics/manifests/ios/Podfile b/firebase_analytics/manifests/ios/Podfile index 755be2a..045ffbe 100644 --- a/firebase_analytics/manifests/ios/Podfile +++ b/firebase_analytics/manifests/ios/Podfile @@ -1 +1 @@ -pod 'FirebaseAnalytics', '8.13.0' +pod 'FirebaseAnalytics', '10.19.0' diff --git a/firebase_analytics/src/com_defold_firebase_analytics_FirebaseAnalyticsJNI.h b/firebase_analytics/src/com_defold_firebase_analytics_FirebaseAnalyticsJNI.h new file mode 100644 index 0000000..02e5e2b --- /dev/null +++ b/firebase_analytics/src/com_defold_firebase_analytics_FirebaseAnalyticsJNI.h @@ -0,0 +1,25 @@ +#if defined(DM_PLATFORM_ANDROID) + +#include +/* Header for class com_defold_firebase_analytics_FirebaseAnalyticsJNI */ + +#ifndef COM_DEFOLD_FIREBASE_ANALYTICS_FIREBASEANALYTICSJNI_H +#define COM_DEFOLD_FIREBASE_ANALYTICS_FIREBASEANALYTICSJNI_H +#ifdef __cplusplus +extern "C" { +#endif + /* + * Class: com_defold_firebase_analytics_FirebaseAnalyticsJNI + * Method: firebaseAddToQueue_first_arg + * Signature: (ILjava/lang/String;I)V + */ + + JNIEXPORT void JNICALL Java_com_defold_firebase_analytics_FirebaseAnalyticsJNI_firebaseAddToQueue + (JNIEnv *, jclass, jint, jstring); + +#ifdef __cplusplus +} +#endif +#endif + +#endif diff --git a/firebase_analytics/src/firebase_analytics.cpp b/firebase_analytics/src/firebase_analytics.cpp index a400d59..9dc63c7 100644 --- a/firebase_analytics/src/firebase_analytics.cpp +++ b/firebase_analytics/src/firebase_analytics.cpp @@ -1,441 +1,449 @@ +#define EXTENSION_NAME FirebaseAnalyticsExt #define LIB_NAME "FirebaseAnalytics" +#define MODULE_NAME "firebase" #define DLIB_LOG_DOMAIN LIB_NAME -#include "luautils.h" + +#include + #include #include +#include "luautils.h" -#if defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) +#include "firebase_analytics_private.h" +#include "firebase_analytics_callback.h" -#include +#if defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) -#include "firebase/app.h" -#include "firebase/analytics.h" #include "firebase/analytics/event_names.h" #include "firebase/analytics/parameter_names.h" -#include "firebase/future.h" +#include "firebase/analytics/user_property_names.h" +using namespace firebase::analytics; -#include "firebase.h" -#include "firebase_analytics.h" +namespace dmFirebaseAnalytics { +// Limitations docs: +// https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param +const size_t MAX_ELEMENTS = 25; +const size_t MAX_EVENT_NAME_LENGTH = 40; +const size_t MAX_PARAM_LENGTH = 40; +const size_t MAX_VALUE_LENGTH = 100; -using namespace firebase; -using namespace firebase::analytics; +static void CheckIfEventNameValid(lua_State* L, const char* event_name) { + if (strlen(event_name) > MAX_EVENT_NAME_LENGTH) + { + luaL_error(L, "Event name '%s' longer than maximum allowed '%d' symbols", event_name, MAX_EVENT_NAME_LENGTH); + } +} -static dmScript::LuaCallbackInfo* g_FirebaseAnalytics_InstanceIdCallback; -static bool g_FirebaseAnalytics_Initialized = false; +static void CheckIfParamValid(lua_State* L, const char* param_key, const char* event_name) { + if (strlen(param_key) > MAX_PARAM_LENGTH) + { + luaL_error(L, "Parameter name '%s' for event '%s' longer than maximum allowed '%d' symbols", param_key, event_name, MAX_PARAM_LENGTH); + } +} -#if defined(DM_PLATFORM_ANDROID) -void FirebaseAnalytics_Safe_LogEvent(lua_State*, const char* name, const firebase::analytics::Parameter* params, size_t number_of_parameters) +static void CheckIfValueValid(lua_State* L, const char* param_value, const char* event_name, const char* param_key) { + if (strlen(param_value) > MAX_VALUE_LENGTH) + { + luaL_error(L, "Parameter value '%s' with key '%s' for event '%s' longer than maximum allowed '%d' symbols", param_value, param_key, event_name, MAX_VALUE_LENGTH); + } +} + +static int Lua_Initialize(lua_State* L) { + DM_LUA_STACK_CHECK(L, 0); + Initialize(); + return 0; +} + +static int Lua_SetCallback(lua_State* L) { - LogEvent(name, params, number_of_parameters); + DM_LUA_STACK_CHECK(L, 0); + SetLuaCallback(L, 1); + return 0; } -#define THREAD_ATTACHER() dmAndroid::ThreadAttacher threadAttacher; -#else -#define THREAD_ATTACHER() -#endif - -static int FirebaseAnalytics_Init(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - const firebase::App* app = Firebase_GetFirebaseApp(); - analytics::Initialize(*app); - g_FirebaseAnalytics_Initialized = true; - return 0; + +static int Lua_GetInstanceId(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + GetInstanceId(); + return 0; } +static int Lua_Log(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* event_name = luaL_checkstring(L, 1); + + CheckIfEventNameValid(L, event_name); -static int FirebaseAnalytics_Analytics_InstanceId(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - g_FirebaseAnalytics_InstanceIdCallback = dmScript::CreateCallback(L, 1); - - analytics::GetAnalyticsInstanceId() - .OnCompletion([](const Future< std::string >& completed_future) { - if (!dmScript::IsCallbackValid(g_FirebaseAnalytics_InstanceIdCallback)) - { - dmLogWarning("Analytics InstanceId callback is not valid"); - return; - } - - if (dmScript::SetupCallback(g_FirebaseAnalytics_InstanceIdCallback)) - { - lua_State* L = dmScript::GetCallbackLuaContext(g_FirebaseAnalytics_InstanceIdCallback); - - if (completed_future.error() == 0) { - lua_pushstring(L, completed_future.result()->c_str()); - int ret = lua_pcall(L, 2, 0, 0); - if (ret != 0) { - lua_pop(L, 1); - } - } - else { - dmLogError("%d: %s", completed_future.error(), completed_future.error_message()); - lua_pushnil(L); - lua_pushstring(L, completed_future.error_message()); - int ret = lua_pcall(L, 3, 0, 0); - if (ret != 0) { - lua_pop(L, 2); - } - } - dmScript::TeardownCallback(g_FirebaseAnalytics_InstanceIdCallback); - } - - dmScript::DestroyCallback(g_FirebaseAnalytics_InstanceIdCallback); - g_FirebaseAnalytics_InstanceIdCallback = 0; - }); - return 0; + OpenEvent(); + SendEvent(event_name); + CloseEvent(); + return 0; } -static int FirebaseAnalytics_Analytics_Log(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* name = luaL_checkstring(L, 1); - THREAD_ATTACHER(); - LogEvent(name); - return 0; +static int Lua_LogString(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* event_name = luaL_checkstring(L, 1); + const char* param_name = luaL_checkstring(L, 2); + const char* param = luaL_checkstring(L, 3); + + CheckIfEventNameValid(L, event_name); + CheckIfParamValid(L, param_name, event_name); + CheckIfValueValid(L, param, event_name, param_name); + + OpenEvent(); + AddParamString(param_name, param); + SendEvent(event_name); + CloseEvent(); + return 0; } -static int FirebaseAnalytics_Analytics_LogString(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* name = luaL_checkstring(L, 1); - const char* parameter_name = luaL_checkstring(L, 2); - const char* parameter_value = luaL_checkstring(L, 3); - THREAD_ATTACHER(); - LogEvent(name, parameter_name, parameter_value); - return 0; +static int Lua_LogInt(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* event_name = luaL_checkstring(L, 1); + const char* param_name = luaL_checkstring(L, 2); + const int param = luaL_checkint(L, 3); + + CheckIfEventNameValid(L, event_name); + CheckIfParamValid(L, param_name, event_name); + + OpenEvent(); + AddParamInt(param_name, param); + SendEvent(event_name); + CloseEvent(); + return 0; } -static int FirebaseAnalytics_Analytics_LogInt(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* name = luaL_checkstring(L, 1); - const char* parameter_name = luaL_checkstring(L, 2); - const int parameter_value = luaL_checkint(L, 3); - THREAD_ATTACHER(); - LogEvent(name, parameter_name, parameter_value); - return 0; +static int Lua_LogNumber(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* event_name = luaL_checkstring(L, 1); + const char* param_name = luaL_checkstring(L, 2); + const lua_Number param = luaL_checknumber(L, 3); + + CheckIfEventNameValid(L, event_name); + CheckIfParamValid(L, param_name, event_name); + + OpenEvent(); + AddParamNumber(param_name, param); + SendEvent(event_name); + CloseEvent(); + return 0; } -static int FirebaseAnalytics_Analytics_LogNumber(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* name = luaL_checkstring(L, 1); - const char* parameter_name = luaL_checkstring(L, 2); - const lua_Number parameter_value = luaL_checknumber(L, 3); - THREAD_ATTACHER(); - LogEvent(name, parameter_name, parameter_value); - return 0; +static int Lua_LogTable(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + + const char* event_name = luaL_checkstring(L, 1); + CheckIfEventNameValid(L, event_name); + luaL_checktype(L, 2, LUA_TTABLE); + OpenEvent(); + lua_pushvalue(L, 2); + lua_pushnil(L); + int size = 0; + while (lua_next(L, -2) != 0) + { + if (size == MAX_ELEMENTS) { + lua_pop(L, 2); + CloseEvent(); + return luaL_error(L, "Too many parameters in '%s'", event_name); + } + const char* param_name = lua_tostring(L, -2); + // Can't use CheckIfParamValid here, because cleanup needed if it's invalid + if (strlen(param_name) > MAX_PARAM_LENGTH) + { + lua_pop(L, 3); + CloseEvent(); + CheckIfParamValid(L, param_name, event_name); + return 0; + } + int t = lua_type(L, -1); + const char* param_value; + switch (t) { + case LUA_TSTRING: + param_value = lua_tostring(L, -1); + // Can't use CheckIfValueValid here, because cleanup needed if it's invalid + if (strlen(param_value) > MAX_VALUE_LENGTH) + { + lua_pop(L, 3); + CloseEvent(); + CheckIfValueValid(L, param_value, event_name, param_name); + return 0; + } + AddParamString(param_name, param_value); + break; + case LUA_TBOOLEAN: + AddParamInt(param_name, lua_toboolean(L, -1)); + break; + case LUA_TNUMBER: + AddParamNumber(param_name, lua_tonumber(L, -1)); + break; + default: /* other values */ + lua_pop(L, 3); + CloseEvent(); + return luaL_error(L, "Wrong type for table attribute '%s', type: '%s' in event '%s'", param_name, luaL_typename(L, -1), event_name); + break; + } + lua_pop(L, 1); + ++size; + } + + SendEvent(event_name); + CloseEvent(); + + lua_pop(L, 1); + + return 0; } -const size_t MAX_ELEMENTS = 25; //Specified in Firebase docs - -static int FirebaseAnalytics_Analytics_LogTable(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - - analytics::Parameter params[MAX_ELEMENTS]; - - const char* name = luaL_checkstring(L, 1); - luaL_checktype(L, 2, LUA_TTABLE); - - lua_pushvalue(L, 2); - lua_pushnil(L); - int size = 0; - while (lua_next(L, -2) != 0) - { - if (size == MAX_ELEMENTS) { - char msg[256]; - snprintf(msg, sizeof(msg), "Too many parameters in '%s'", name); - luaL_error(L, msg); - lua_pop(L, 2); - return 0; - } - const char* k = lua_tostring(L, -2); - int t = lua_type(L, -1); - switch (t) { - case LUA_TSTRING: - params[size] = analytics::Parameter(k, lua_tostring(L, -1)); - break; - case LUA_TBOOLEAN: - params[size] = analytics::Parameter(k, lua_toboolean(L, -1) != 0); - break; - case LUA_TNUMBER: - params[size] = analytics::Parameter(k, lua_tonumber(L, -1)); - break; - default: /* other values */ - char msg[256]; - snprintf(msg, sizeof(msg), "Wrong type for table attribute '%s' , type: '%s'", k, luaL_typename(L, -1)); - luaL_error(L, msg); - lua_pop(L, 3); - return 0; - break; - } - lua_pop(L, 1); - ++size; - } - THREAD_ATTACHER(); - FirebaseAnalytics_Safe_LogEvent(L, name, params, size); - - lua_pop(L, 1); - return 0; +static int Lua_Reset(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + ResetAnalyticsData(); + return 0; } -static int FirebaseAnalytics_Analytics_Reset(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - THREAD_ATTACHER(); - ResetAnalyticsData(); - return 0; +static int Lua_SetEnabled(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + int enabled = lua_toboolean(L, 1); + SetAnalyticsCollectionEnabled(enabled); + return 0; } -static int FirebaseAnalytics_Analytics_SetEnabled(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - int enabled = lua_toboolean(L, 1); - THREAD_ATTACHER(); - SetAnalyticsCollectionEnabled((bool)enabled); - return 0; +static int Lua_SetUserProperty(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* name = luaL_checkstring(L, 1); + const char* property = luaL_checkstring(L, 2); + SetUserProperty(name, property); + return 0; } -static int FirebaseAnalytics_Analytics_SetScreen(lua_State* L) { - dmLogWarning("set_screen() has been deprecated. Use log_string() and EVENT_SCREENVIEW instead."); - return 0; +static int Lua_SetUserId(lua_State* L) +{ + DM_LUA_STACK_CHECK(L, 0); + const char* user_id = luaL_checkstring(L, 1); + SetUserId(user_id); + return 0; } -static int FirebaseAnalytics_Analytics_SetUserProperty(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* name = luaL_checkstring(L, 1); - const char* property = luaL_checkstring(L, 2); - THREAD_ATTACHER(); - SetUserProperty(name, property); - return 0; +static const luaL_reg Module_methods[] = +{ + {"initialize", Lua_Initialize}, + {"set_callback", Lua_SetCallback}, + + {"get_id", Lua_GetInstanceId}, + {"log", Lua_Log}, + {"log_string", Lua_LogString}, + {"log_int", Lua_LogInt}, + {"log_number", Lua_LogNumber}, + {"log_table", Lua_LogTable}, + {"reset", Lua_Reset}, + {"set_enabled", Lua_SetEnabled}, + {"set_user_property", Lua_SetUserProperty}, + {"set_user_id", Lua_SetUserId}, + {0, 0} +}; + + +static void LuaInit(lua_State* L) { + DM_LUA_STACK_CHECK(L, 0); + + // get or create firebase global table + lua_getglobal(L, "firebase"); + if (lua_isnil(L, lua_gettop(L))) + { + lua_pushstring(L, "firebase"); + lua_newtable(L); + lua_settable(L, LUA_GLOBALSINDEX); + lua_pop(L, 1); + lua_getglobal(L, "firebase"); + } + + lua_pushstring(L, "analytics"); + lua_newtable(L); + + luaL_register(L, NULL, Module_methods); + + // From firebase/analytics/event_names.h + lua_pushtablestringstring(L, "EVENT_ADIMPRESSION", kEventAdImpression); + lua_pushtablestringstring(L, "EVENT_ADDPAYMENTINFO", kEventAddPaymentInfo); + lua_pushtablestringstring(L, "EVENT_ADDSHIPPINGINFO", kEventAddShippingInfo); + lua_pushtablestringstring(L, "EVENT_ADDTOCART", kEventAddToCart); + lua_pushtablestringstring(L, "EVENT_ADDTOWISHLIST", kEventAddToWishlist); + lua_pushtablestringstring(L, "EVENT_APPOPEN", kEventAppOpen); + lua_pushtablestringstring(L, "EVENT_BEGINCHECKOUT", kEventBeginCheckout); + lua_pushtablestringstring(L, "EVENT_CAMPAIGNDETAILS", kEventCampaignDetails); + lua_pushtablestringstring(L, "EVENT_EARNVIRTUALCURRENCY", kEventEarnVirtualCurrency); + lua_pushtablestringstring(L, "EVENT_GENERATELEAD", kEventGenerateLead); + lua_pushtablestringstring(L, "EVENT_JOINGROUP", kEventJoinGroup); + lua_pushtablestringstring(L, "EVENT_LEVELEND", kEventLevelEnd); + lua_pushtablestringstring(L, "EVENT_LEVELSTART", kEventLevelStart); + lua_pushtablestringstring(L, "EVENT_LEVELUP", kEventLevelUp); + lua_pushtablestringstring(L, "EVENT_LOGIN", kEventLogin); + lua_pushtablestringstring(L, "EVENT_POSTSCORE", kEventPostScore); + lua_pushtablestringstring(L, "EVENT_PURCHASE", kEventPurchase); + lua_pushtablestringstring(L, "EVENT_REFUND", kEventRefund); + lua_pushtablestringstring(L, "EVENT_REMOVEFROMCART", kEventRemoveFromCart); + lua_pushtablestringstring(L, "EVENT_SCREENVIEW", kEventScreenView); + lua_pushtablestringstring(L, "EVENT_SEARCH", kEventSearch); + lua_pushtablestringstring(L, "EVENT_SELECTCONTENT", kEventSelectContent); + lua_pushtablestringstring(L, "EVENT_SELECTITEM", kEventSelectItem); + lua_pushtablestringstring(L, "EVENT_SELECTPROMOTION", kEventSelectPromotion); + lua_pushtablestringstring(L, "EVENT_SHARE", kEventShare); + lua_pushtablestringstring(L, "EVENT_SIGNUP", kEventSignUp); + lua_pushtablestringstring(L, "EVENT_SPENDVIRTUALCURRENCY", kEventSpendVirtualCurrency); + lua_pushtablestringstring(L, "EVENT_TUTORIALBEGIN", kEventTutorialBegin); + lua_pushtablestringstring(L, "EVENT_TUTORIALCOMPLETE", kEventTutorialComplete); + lua_pushtablestringstring(L, "EVENT_UNLOCKACHIEVEMENT", kEventUnlockAchievement); + lua_pushtablestringstring(L, "EVENT_VIEWCART", kEventViewCart); + lua_pushtablestringstring(L, "EVENT_VIEWITEM", kEventViewItem); + lua_pushtablestringstring(L, "EVENT_VIEWITEMLIST", kEventViewItemList); + lua_pushtablestringstring(L, "EVENT_VIEWPROMOTION", kEventViewPromotion); + lua_pushtablestringstring(L, "EVENT_VIEWSEARCHRESULTS", kEventViewSearchResults); + // From firebase/analytics/parameter_names.h + lua_pushtablestringstring(L, "PARAM_ADFORMAT", kParameterAdFormat); + lua_pushtablestringstring(L, "PARAM_ADNETWORKCLICKID", kParameterAdNetworkClickID); + lua_pushtablestringstring(L, "PARAM_ADPLATFORM", kParameterAdPlatform); + lua_pushtablestringstring(L, "PARAM_ADSOURCE", kParameterAdSource); + lua_pushtablestringstring(L, "PARAM_ADUNITNAME", kParameterAdUnitName); + lua_pushtablestringstring(L, "PARAM_AFFILIATION", kParameterAffiliation); + lua_pushtablestringstring(L, "PARAM_CP1", kParameterCP1); + lua_pushtablestringstring(L, "PARAM_CAMPAIGN", kParameterCampaign); + lua_pushtablestringstring(L, "PARAM_CAMPAIGNID", kParameterCampaignID); + lua_pushtablestringstring(L, "PARAM_CHARACTER", kParameterCharacter); + lua_pushtablestringstring(L, "PARAM_CONTENT", kParameterContent); + lua_pushtablestringstring(L, "PARAM_CONTENTTYPE", kParameterContentType); + lua_pushtablestringstring(L, "PARAM_COUPON", kParameterCoupon); + lua_pushtablestringstring(L, "PARAM_CREATIVEFORMAT", kParameterCreativeFormat); + lua_pushtablestringstring(L, "PARAM_CREATIVENAME", kParameterCreativeName); + lua_pushtablestringstring(L, "PARAM_CREATIVESLOT", kParameterCreativeSlot); + lua_pushtablestringstring(L, "PARAM_CURRENCY", kParameterCurrency); + lua_pushtablestringstring(L, "PARAM_DESTINATION", kParameterDestination); + lua_pushtablestringstring(L, "PARAM_DISCOUNT", kParameterDiscount); + lua_pushtablestringstring(L, "PARAM_ENDDATE", kParameterEndDate); + lua_pushtablestringstring(L, "PARAM_EXTENDSESSION", kParameterExtendSession); + lua_pushtablestringstring(L, "PARAM_FLIGHTNUMBER", kParameterFlightNumber); + lua_pushtablestringstring(L, "PARAM_GROUPID", kParameterGroupID); + lua_pushtablestringstring(L, "PARAM_INDEX", kParameterIndex); + lua_pushtablestringstring(L, "PARAM_ITEMBRAND", kParameterItemBrand); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY", kParameterItemCategory); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY2", kParameterItemCategory2); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY3", kParameterItemCategory3); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY4", kParameterItemCategory4); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY5", kParameterItemCategory5); + lua_pushtablestringstring(L, "PARAM_ITEMID", kParameterItemID); + lua_pushtablestringstring(L, "PARAM_ITEMLISTID", kParameterItemListID); + lua_pushtablestringstring(L, "PARAM_ITEMLISTNAME", kParameterItemListName); + lua_pushtablestringstring(L, "PARAM_ITEMNAME", kParameterItemName); + lua_pushtablestringstring(L, "PARAM_ITEMVARIANT", kParameterItemVariant); + lua_pushtablestringstring(L, "PARAM_ITEMS", kParameterItems); + lua_pushtablestringstring(L, "PARAM_LEVEL", kParameterLevel); + lua_pushtablestringstring(L, "PARAM_LEVELNAME", kParameterLevelName); + lua_pushtablestringstring(L, "PARAM_LOCATION", kParameterLocation); + lua_pushtablestringstring(L, "PARAM_LOCATIONID", kParameterLocationID); + lua_pushtablestringstring(L, "PARAM_MARKETINGTACTIC", kParameterMarketingTactic); + lua_pushtablestringstring(L, "PARAM_MEDIUM", kParameterMedium); + lua_pushtablestringstring(L, "PARAM_METHOD", kParameterMethod); + lua_pushtablestringstring(L, "PARAM_NUMBEROFNIGHTS", kParameterNumberOfNights); + lua_pushtablestringstring(L, "PARAM_NUMBEROFPASSENGERS", kParameterNumberOfPassengers); + lua_pushtablestringstring(L, "PARAM_NUMBEROFROOMS", kParameterNumberOfRooms); + lua_pushtablestringstring(L, "PARAM_ORIGIN", kParameterOrigin); + lua_pushtablestringstring(L, "PARAM_PAYMENTTYPE", kParameterPaymentType); + lua_pushtablestringstring(L, "PARAM_PRICE", kParameterPrice); + lua_pushtablestringstring(L, "PARAM_PROMOTIONID", kParameterPromotionID); + lua_pushtablestringstring(L, "PARAM_PROMOTIONNAME", kParameterPromotionName); + lua_pushtablestringstring(L, "PARAM_QUANTITY", kParameterQuantity); + lua_pushtablestringstring(L, "PARAM_SCORE", kParameterScore); + lua_pushtablestringstring(L, "PARAM_SCREENCLASS", kParameterScreenClass); + lua_pushtablestringstring(L, "PARAM_SCREENNAME", kParameterScreenName); + lua_pushtablestringstring(L, "PARAM_SEARCHTERM", kParameterSearchTerm); + lua_pushtablestringstring(L, "PARAM_SHIPPING", kParameterShipping); + lua_pushtablestringstring(L, "PARAM_SHIPPINGTIER", kParameterShippingTier); + lua_pushtablestringstring(L, "PARAM_SOURCE", kParameterSource); + lua_pushtablestringstring(L, "PARAM_SOURCEPLATFORM", kParameterSourcePlatform); + lua_pushtablestringstring(L, "PARAM_STARTDATE", kParameterStartDate); + lua_pushtablestringstring(L, "PARAM_SUCCESS", kParameterSuccess); + lua_pushtablestringstring(L, "PARAM_TAX", kParameterTax); + lua_pushtablestringstring(L, "PARAM_TERM", kParameterTerm); + lua_pushtablestringstring(L, "PARAM_TRANSACTIONID", kParameterTransactionID); + lua_pushtablestringstring(L, "PARAM_TRAVELCLASS", kParameterTravelClass); + lua_pushtablestringstring(L, "PARAM_VALUE", kParameterValue); + lua_pushtablestringstring(L, "PARAM_VIRTUALCURRENCYNAME", kParameterVirtualCurrencyName); + // From firebase/analytics/user_property_names.h + lua_pushtablestringstring(L, "PROP_ALLOWADPERSONALIZATIONSIGNALS", kUserPropertyAllowAdPersonalizationSignals); + lua_pushtablestringstring(L, "PROP_SIGNUPMETHOD", kUserPropertySignUpMethod); + + + #define SETCONSTANT(name) \ + lua_pushnumber(L, (lua_Number) name); \ + lua_setfield(L, -2, #name); \ + + SETCONSTANT(MSG_ERROR) + SETCONSTANT(MSG_INSTANCE_ID) + + #undef SETCONSTANT + + lua_settable(L, -3); + + lua_pop(L, 1); } -static int FirebaseAnalytics_Analytics_SetUserId(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - if (!g_FirebaseAnalytics_Initialized) - { - dmLogWarning("Firebase Analytics has not been initialized! Make sure to call firebase.analytics.init()."); - return 0; - } - const char* user_id = luaL_checkstring(L, 1); - THREAD_ATTACHER(); - SetUserId(user_id); - return 0; + +//--------------------------- Extension callbacks -------------------------// + +dmExtension::Result AppInitializeFirebaseExtension(dmExtension::AppParams* params) { + return dmExtension::RESULT_OK; } +dmExtension::Result InitializeFirebaseExtension(dmExtension::Params* params) { + dmLogInfo("Initialize Firebase Analytics"); -static void LuaInit(lua_State* L) { - DM_LUA_STACK_CHECK(L, 0); - - // get or create firebase global table - lua_getglobal(L, "firebase"); - if (lua_isnil(L, lua_gettop(L))) - { - lua_pushstring(L, "firebase"); - lua_newtable(L); - lua_settable(L, LUA_GLOBALSINDEX); - lua_pop(L, 1); - lua_getglobal(L, "firebase"); - } - - lua_pushstring(L, "analytics"); - lua_newtable(L); - lua_pushtablestringfunction(L, "init", FirebaseAnalytics_Init); - lua_pushtablestringfunction(L, "get_id", FirebaseAnalytics_Analytics_InstanceId); - lua_pushtablestringfunction(L, "log", FirebaseAnalytics_Analytics_Log); - lua_pushtablestringfunction(L, "log_string", FirebaseAnalytics_Analytics_LogString); - lua_pushtablestringfunction(L, "log_int", FirebaseAnalytics_Analytics_LogInt); - lua_pushtablestringfunction(L, "log_number", FirebaseAnalytics_Analytics_LogNumber); - lua_pushtablestringfunction(L, "log_table", FirebaseAnalytics_Analytics_LogTable); - lua_pushtablestringfunction(L, "reset", FirebaseAnalytics_Analytics_Reset); - lua_pushtablestringfunction(L, "set_enabled", FirebaseAnalytics_Analytics_SetEnabled); - lua_pushtablestringfunction(L, "set_screen", FirebaseAnalytics_Analytics_SetScreen); - lua_pushtablestringfunction(L, "set_user_property", FirebaseAnalytics_Analytics_SetUserProperty); - lua_pushtablestringfunction(L, "set_user_id", FirebaseAnalytics_Analytics_SetUserId); - - // From firebase/analytics/event_names.h - lua_pushtablestringstring(L, "EVENT_ADDPAYMENTINFO", kEventAddPaymentInfo); - lua_pushtablestringstring(L, "EVENT_ADDTOCART", kEventAddToCart); - lua_pushtablestringstring(L, "EVENT_ADDTOWISHLIST", kEventAddToWishlist); - lua_pushtablestringstring(L, "EVENT_ADIMPRESSION", kEventAdImpression); - lua_pushtablestringstring(L, "EVENT_APPOPEN", kEventAppOpen); - lua_pushtablestringstring(L, "EVENT_BEGINCHECKOUT", kEventBeginCheckout); - lua_pushtablestringstring(L, "EVENT_CAMPAIGNDETAILS", kEventCampaignDetails); - lua_pushtablestringstring(L, "EVENT_CHECKOUTPROGRESS", kEventCheckoutProgress); - lua_pushtablestringstring(L, "EVENT_EARNVIRTUALCURRENCY", kEventEarnVirtualCurrency); - lua_pushtablestringstring(L, "EVENT_ECOMMERCEPURCHASE", kEventEcommercePurchase); - lua_pushtablestringstring(L, "EVENT_GENERATELEAD", kEventGenerateLead); - lua_pushtablestringstring(L, "EVENT_JOINGROUP", kEventJoinGroup); - lua_pushtablestringstring(L, "EVENT_LEVELEND", kEventLevelEnd); - lua_pushtablestringstring(L, "EVENT_LEVELSTART", kEventLevelStart); - lua_pushtablestringstring(L, "EVENT_LEVELUP", kEventLevelUp); - lua_pushtablestringstring(L, "EVENT_LOGIN", kEventLogin); - lua_pushtablestringstring(L, "EVENT_POSTSCORE", kEventPostScore); - lua_pushtablestringstring(L, "EVENT_PRESENTOFFER", kEventPresentOffer); - lua_pushtablestringstring(L, "EVENT_PURCHASEREFUND", kEventPurchaseRefund); - lua_pushtablestringstring(L, "EVENT_REMOVEFROMCART", kEventRemoveFromCart); - lua_pushtablestringstring(L, "EVENT_SCREENVIEW", kEventScreenView); - lua_pushtablestringstring(L, "EVENT_SEARCH", kEventSearch); - lua_pushtablestringstring(L, "EVENT_SELECTCONTENT", kEventSelectContent); - lua_pushtablestringstring(L, "EVENT_SETCHECKOUTOPTION", kEventSetCheckoutOption); - lua_pushtablestringstring(L, "EVENT_SHARE", kEventShare); - lua_pushtablestringstring(L, "EVENT_SIGNUP", kEventSignUp); - lua_pushtablestringstring(L, "EVENT_SPENDVIRTUALCURRENCY", kEventSpendVirtualCurrency); - lua_pushtablestringstring(L, "EVENT_TUTORIALBEGIN", kEventTutorialBegin); - lua_pushtablestringstring(L, "EVENT_TUTORIALCOMPLETE", kEventTutorialComplete); - lua_pushtablestringstring(L, "EVENT_UNLOCKACHIEVEMENT", kEventUnlockAchievement); - lua_pushtablestringstring(L, "EVENT_VIEWITEM", kEventViewItem); - lua_pushtablestringstring(L, "EVENT_VIEWITEMLIST", kEventViewItemList); - lua_pushtablestringstring(L, "EVENT_VIEWSEARCHRESULTS", kEventViewSearchResults); - lua_pushtablestringstring(L, "EVENT_ADDSHIPPINGINFO", kEventAddShippingInfo); - lua_pushtablestringstring(L, "EVENT_PURCHASE", kEventPurchase); - lua_pushtablestringstring(L, "EVENT_REFUND", kEventRefund); - lua_pushtablestringstring(L, "EVENT_SELECTITEM", kEventSelectItem); - lua_pushtablestringstring(L, "EVENT_SELECTPROMOTION", kEventSelectPromotion); - lua_pushtablestringstring(L, "EVENT_VIEWCART", kEventViewCart); - lua_pushtablestringstring(L, "EVENT_VIEWPROMOTION", kEventViewPromotion); - - // From firebase/analytics/parameter_names.h - lua_pushtablestringstring(L, "PARAM_ACHIEVEMENTID", kParameterAchievementID); - lua_pushtablestringstring(L, "PARAM_ADFORMAT", kParameterAdFormat); - lua_pushtablestringstring(L, "PARAM_ADNETWORKCLICKID", kParameterAdNetworkClickID); - lua_pushtablestringstring(L, "PARAM_ADPLATFORM", kParameterAdPlatform); - lua_pushtablestringstring(L, "PARAM_ADSOURCE", kParameterAdSource); - lua_pushtablestringstring(L, "PARAM_ADUNITNAME", kParameterAdUnitName); - lua_pushtablestringstring(L, "PARAM_AFFILIATION", kParameterAffiliation); - lua_pushtablestringstring(L, "PARAM_CAMPAIGN", kParameterCampaign); - lua_pushtablestringstring(L, "PARAM_CHARACTER", kParameterCharacter); - lua_pushtablestringstring(L, "PARAM_CHECKOUTSTEP", kParameterCheckoutStep); - lua_pushtablestringstring(L, "PARAM_CHECKOUTOPTION", kParameterCheckoutOption); - lua_pushtablestringstring(L, "PARAM_CONTENT", kParameterContent); - lua_pushtablestringstring(L, "PARAM_CONTENTTYPE", kParameterContentType); - lua_pushtablestringstring(L, "PARAM_COUPON", kParameterCoupon); - lua_pushtablestringstring(L, "PARAM_CP1", kParameterCP1); - lua_pushtablestringstring(L, "PARAM_CREATIVENAME", kParameterCreativeName); - lua_pushtablestringstring(L, "PARAM_CREATIVESLOT", kParameterCreativeSlot); - lua_pushtablestringstring(L, "PARAM_CURRENCY", kParameterCurrency); - lua_pushtablestringstring(L, "PARAM_DESTINATION", kParameterDestination); - lua_pushtablestringstring(L, "PARAM_ENDDATE", kParameterEndDate); - lua_pushtablestringstring(L, "PARAM_FLIGHTNUMBER", kParameterFlightNumber); - lua_pushtablestringstring(L, "PARAM_GROUPID", kParameterGroupID); - lua_pushtablestringstring(L, "PARAM_INDEX", kParameterIndex); - lua_pushtablestringstring(L, "PARAM_ITEMBRAND", kParameterItemBrand); - lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY", kParameterItemCategory); - lua_pushtablestringstring(L, "PARAM_ITEMID", kParameterItemID); - lua_pushtablestringstring(L, "PARAM_ITEMLOCATIONID", kParameterItemLocationID); - lua_pushtablestringstring(L, "PARAM_ITEMNAME", kParameterItemName); - lua_pushtablestringstring(L, "PARAM_ITEMLIST", kParameterItemList); - lua_pushtablestringstring(L, "PARAM_ITEMVARIANT", kParameterItemVariant); - lua_pushtablestringstring(L, "PARAM_LEVEL", kParameterLevel); - lua_pushtablestringstring(L, "PARAM_LOCATION", kParameterLocation); - lua_pushtablestringstring(L, "PARAM_MEDIUM", kParameterMedium); - lua_pushtablestringstring(L, "PARAM_NUMBEROFNIGHTS", kParameterNumberOfNights); - lua_pushtablestringstring(L, "PARAM_NUMBEROFPASSENGERS", kParameterNumberOfPassengers); - lua_pushtablestringstring(L, "PARAM_NUMBEROFROOMS", kParameterNumberOfRooms); - lua_pushtablestringstring(L, "PARAM_ORIGIN", kParameterOrigin); - lua_pushtablestringstring(L, "PARAM_PRICE", kParameterPrice); - lua_pushtablestringstring(L, "PARAM_QUANTITY", kParameterQuantity); - lua_pushtablestringstring(L, "PARAM_SCORE", kParameterScore); - lua_pushtablestringstring(L, "PARAM_SCREENCLASS", kParameterScreenClass); - lua_pushtablestringstring(L, "PARAM_SCREENNAME", kParameterScreenName); - lua_pushtablestringstring(L, "PARAM_SEARCHTERM", kParameterSearchTerm); - lua_pushtablestringstring(L, "PARAM_SHIPPING", kParameterShipping); - lua_pushtablestringstring(L, "PARAM_SIGNUPMETHOD", kParameterSignUpMethod); - lua_pushtablestringstring(L, "PARAM_METHOD", kParameterMethod); - lua_pushtablestringstring(L, "PARAM_SOURCE", kParameterSource); - lua_pushtablestringstring(L, "PARAM_STARTDATE", kParameterStartDate); - lua_pushtablestringstring(L, "PARAM_TAX", kParameterTax); - lua_pushtablestringstring(L, "PARAM_TERM", kParameterTerm); - lua_pushtablestringstring(L, "PARAM_TRANSACTIONID", kParameterTransactionID); - lua_pushtablestringstring(L, "PARAM_TRAVELCLASS", kParameterTravelClass); - lua_pushtablestringstring(L, "PARAM_VALUE", kParameterValue); - lua_pushtablestringstring(L, "PARAM_VIRTUALCURRENCYNAME", kParameterVirtualCurrencyName); - lua_pushtablestringstring(L, "PARAM_LEVELNAME", kParameterLevelName); - lua_pushtablestringstring(L, "PARAM_SUCCESS", kParameterSuccess); - lua_pushtablestringstring(L, "PARAM_EXTENDSESSION", kParameterExtendSession); - lua_pushtablestringstring(L, "PARAM_DISCOUNT", kParameterDiscount); - lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY2", kParameterItemCategory2); - lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY3", kParameterItemCategory3); - lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY4", kParameterItemCategory4); - lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY5", kParameterItemCategory5); - lua_pushtablestringstring(L, "PARAM_ITEMLISTID", kParameterItemListID); - lua_pushtablestringstring(L, "PARAM_ITEMLISTNAME", kParameterItemListName); - lua_pushtablestringstring(L, "PARAM_ITEMS", kParameterItems); - lua_pushtablestringstring(L, "PARAM_LOCATIONID", kParameterLocationID); - lua_pushtablestringstring(L, "PARAM_PAYMENTTYPE", kParameterPaymentType); - lua_pushtablestringstring(L, "PARAM_PROMOTIONID", kParameterPromotionID); - lua_pushtablestringstring(L, "PARAM_PROMOTIONNAME", kParameterPromotionName); - lua_pushtablestringstring(L, "PARAM_SHIPPINGTIER", kParameterShippingTier); - - lua_settable(L, -3); - - lua_pop(L, 1); // pop "firebase" global table + LuaInit(params->m_L); + Initialize_Ext(); + InitializeCallback(); + + return dmExtension::RESULT_OK; } -#undef THREAD_ATTACHER() -#endif -dmExtension::Result AppInitializeFirebaseAnalyticsExtension(dmExtension::AppParams* params) { - return dmExtension::RESULT_OK; +dmExtension::Result AppFinalizeFirebaseExtension(dmExtension::AppParams* params) { + return dmExtension::RESULT_OK; } -dmExtension::Result InitializeFirebaseAnalyticsExtension(dmExtension::Params* params) { - #if !defined(DM_PLATFORM_ANDROID) && !defined(DM_PLATFORM_IOS) - dmLogInfo("Extension %s is not supported", LIB_NAME); - #else - dmLogInfo("Extension %s is supported", LIB_NAME); - LuaInit(params->m_L); - #endif - return dmExtension::RESULT_OK; +dmExtension::Result FinalizeFirebaseExtension(dmExtension::Params* params) { + FinalizeCallback(); + return dmExtension::RESULT_OK; } -dmExtension::Result AppFinalizeFirebaseAnalyticsExtension(dmExtension::AppParams* params) { - return dmExtension::RESULT_OK; +dmExtension::Result UpdateFirebaseExtension(dmExtension::Params* params) { + UpdateCallback(); + return dmExtension::RESULT_OK; } -dmExtension::Result FinalizeFirebaseAnalyticsExtension(dmExtension::Params* params) { - return dmExtension::RESULT_OK; +} // namespace dmFirebaseAnalytics + +DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, dmFirebaseAnalytics::AppInitializeFirebaseExtension, dmFirebaseAnalytics::AppFinalizeFirebaseExtension, dmFirebaseAnalytics::InitializeFirebaseExtension, dmFirebaseAnalytics::UpdateFirebaseExtension, 0, dmFirebaseAnalytics::FinalizeFirebaseExtension) + +#else // defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) + +//-------------------- Not supported for other platforms --------------------// + +static dmExtension::Result InitializeFirebaseAnalytics(dmExtension::Params* params) +{ + dmLogInfo("Registered extension Firebase Analytics (null)"); + return dmExtension::RESULT_OK; } -dmExtension::Result UpdateFirebaseAnalyticsExtension(dmExtension::Params* params) { - return dmExtension::RESULT_OK; +static dmExtension::Result FinalizeFirebaseAnalytics(dmExtension::Params* params) +{ + return dmExtension::RESULT_OK; } -DM_DECLARE_EXTENSION(FirebaseAnalytics, LIB_NAME, AppInitializeFirebaseAnalyticsExtension, AppFinalizeFirebaseAnalyticsExtension, InitializeFirebaseAnalyticsExtension, UpdateFirebaseAnalyticsExtension, 0, FinalizeFirebaseAnalyticsExtension) +DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, 0, 0, InitializeFirebaseAnalytics, 0, 0, FinalizeFirebaseAnalytics) + +#endif // defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) diff --git a/firebase_analytics/src/firebase_analytics.mm b/firebase_analytics/src/firebase_analytics.mm index 8bca0f8..df81354 100644 --- a/firebase_analytics/src/firebase_analytics.mm +++ b/firebase_analytics/src/firebase_analytics.mm @@ -1,52 +1,122 @@ #include #if defined(DM_PLATFORM_IOS) +#include -#include "firebase/app.h" -#include "firebase/analytics.h" -#include "firebase/analytics/event_names.h" -#include "firebase/analytics/parameter_names.h" -#include "firebase/future.h" +#include "firebase_analytics_callback.h" -#include "firebase.h" -#include "firebase_analytics.h" +namespace dmFirebaseAnalytics { -#import +void SendSimpleMessage(Message msg, id obj) { + NSError* error; + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:obj options:(NSJSONWritingOptions)0 error:&error]; -using namespace firebase; -using namespace firebase::analytics; - -void FirebaseAnalytics_Safe_LogEvent(lua_State* L, const char* name, const firebase::analytics::Parameter* params, size_t number_of_parameters) -{ - @try + if (jsonData) { - LogEvent(name, params, number_of_parameters); + NSString* nsstring = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + AddToQueueCallback(msg, (const char*)[nsstring UTF8String]); + [nsstring release]; } - @catch(...) + else { - dmLogError("Exception occurred for event '%s' with parameters:", name) - - for(size_t index = 0; index < number_of_parameters; ++index) + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setObject:error.localizedDescription forKey:@"error"]; + NSError* error2; + NSData* errorJsonData = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingOptions)0 error:&error2]; + if (errorJsonData) { - const auto& parameter = params[index]; - const auto& value = parameter.value; - - if (value.is_double()) - { - dmLogError("\t'%s' = %f", parameter.name, value.double_value()) - } - else if (value.is_string() || value.is_bool() || value.is_null()) - { - dmLogError("\t'%s' = '%s'", parameter.name, value.AsString().string_value()); - } - else - { - dmLogError("\t'%s' = unsupported param type", parameter.name) - } + NSString* nsstringError = [[NSString alloc] initWithData:errorJsonData encoding:NSUTF8StringEncoding]; + AddToQueueCallback(MSG_ERROR, (const char*)[nsstringError UTF8String]); + [nsstringError release]; } + else + { + AddToQueueCallback(MSG_ERROR, [[NSString stringWithFormat:@"{ \"error\": \"Error while converting simple message to JSON.\"}"] UTF8String]); + } + } +} + +void SendSimpleMessage(Message msg, NSString *key, NSString *value) { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setObject:value forKey:key]; + SendSimpleMessage(msg, dict); +} + +void Initialize_Ext() {} +void Initialize() {} + +void GetInstanceId() { + SendSimpleMessage(MSG_INSTANCE_ID, @"instance_id", [FIRAnalytics appInstanceID]); +} + +void SetUserId(const char* id) { + [FIRAnalytics setUserID:[NSString stringWithUTF8String:id]]; +} + +void SetUserProperty(const char* name, const char* value) { + [FIRAnalytics setUserPropertyString:[NSString stringWithUTF8String:value] forName:[NSString stringWithUTF8String:name]]; +} + +void ResetAnalyticsData() { + [FIRAnalytics resetAnalyticsData]; +} + +void SetAnalyticsCollectionEnabled(bool enabled) { + [FIRAnalytics setAnalyticsCollectionEnabled:enabled? YES : NO]; +} - luaL_error(L, "firebase: log event exception. \"%s\"", name); +// --- + +NSMutableDictionary *g_bundle; + +static void ReleaseBundle() { + for (NSString *key in g_bundle) + { + [[g_bundle objectForKey:key] release]; + [key release]; + } + [g_bundle release]; + g_bundle = nil; +} + +void OpenEvent() { + if (g_bundle != nil) + { + ReleaseBundle(); } + g_bundle = [[NSMutableDictionary alloc] init]; } +void AddParamString(const char* param_name, const char* param) { + NSString *key = [[NSString alloc] initWithUTF8String:param_name]; + NSString *value = [[NSString alloc] initWithUTF8String:param]; + [g_bundle setObject:value forKey:key]; +} + +void AddParamNumber(const char* param_name, double param) { + NSString *key = [[NSString alloc] initWithUTF8String:param_name]; + NSNumber *value = [[NSNumber alloc] initWithDouble:param]; + [g_bundle setObject:value forKey:key]; +} + +void AddParamInt(const char* param_name, int param) { + NSString *key = [[NSString alloc] initWithUTF8String:param_name]; + NSNumber *value = [[NSNumber alloc] initWithInt:param]; + [g_bundle setObject:value forKey:key]; +} + +void SendEvent(const char* event_name) { + NSString *name = [[NSString alloc] initWithUTF8String:event_name]; + [FIRAnalytics logEventWithName:name parameters:g_bundle]; + [name release]; +} + +void CloseEvent() { + ReleaseBundle(); +} + +// --- + +} //namespace dmFirebaseAnalytics + #endif diff --git a/firebase_analytics/src/firebase_analytics_android.cpp b/firebase_analytics/src/firebase_analytics_android.cpp new file mode 100644 index 0000000..4715abb --- /dev/null +++ b/firebase_analytics/src/firebase_analytics_android.cpp @@ -0,0 +1,188 @@ +#if defined(DM_PLATFORM_ANDROID) + +#include +#include "com_defold_firebase_analytics_FirebaseAnalyticsJNI.h" +#include "firebase_analytics_callback.h" +#include "firebase_analytics_private.h" + +JNIEXPORT void JNICALL Java_com_defold_firebase_analytics_FirebaseAnalyticsJNI_firebaseAddToQueue(JNIEnv * env, jclass cls, jint jmsg, jstring jjson) +{ + const char* json = env->GetStringUTFChars(jjson, 0); + dmFirebaseAnalytics::AddToQueueCallback((dmFirebaseAnalytics::Message)jmsg, json); + env->ReleaseStringUTFChars(jjson, json); +} + +namespace dmFirebaseAnalytics { + + struct FirebaseAnalyticsJNI + { + jobject m_JNI; + + jmethodID m_Initialize; + jmethodID m_GetInstanceId; + jmethodID m_GetID; + jmethodID m_Reset; + jmethodID m_SetEnabled; + jmethodID m_SetUserProperty; + jmethodID m_SetUserID; + + jmethodID m_OpenEvent; + jmethodID m_AddParamNumber; + jmethodID m_AddParamInt; + jmethodID m_AddParamString; + jmethodID m_SendEvent; + jmethodID m_CloseEvent; + }; + + static FirebaseAnalyticsJNI g_firebaseAnalytics; + + static void CallVoidMethod(jobject instance, jmethodID method) + { + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + env->CallVoidMethod(instance, method); + } + + static void CallVoidMethodChar(jobject instance, jmethodID method, const char* cstr) + { + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + + jstring jstr = env->NewStringUTF(cstr); + env->CallVoidMethod(instance, method, jstr); + env->DeleteLocalRef(jstr); + } + + static void CallVoidMethodCharChar(jobject instance, jmethodID method, const char* cstr, const char* cstr1) + { + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + + jstring jstr = env->NewStringUTF(cstr); + jstring jstr1 = env->NewStringUTF(cstr1); + env->CallVoidMethod(instance, method, jstr, jstr1); + env->DeleteLocalRef(jstr); + env->DeleteLocalRef(jstr1); + } + + static void InitJNIMethods(JNIEnv* env, jclass cls) + { + g_firebaseAnalytics.m_Initialize = env->GetMethodID(cls, "initialize", "()V"); + g_firebaseAnalytics.m_GetInstanceId = env->GetMethodID(cls, "getInstanceId", "()V"); + g_firebaseAnalytics.m_SetUserID = env->GetMethodID(cls, "setUserId", "(Ljava/lang/String;)V"); + g_firebaseAnalytics.m_SetEnabled = env->GetMethodID(cls, "setAnalyticsCollectionEnabled", "(Z)V"); + g_firebaseAnalytics.m_SetUserProperty = env->GetMethodID(cls, "setUserProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); + g_firebaseAnalytics.m_Reset = env->GetMethodID(cls, "resetAnalyticsData", "()V"); + + g_firebaseAnalytics.m_OpenEvent = env->GetMethodID(cls, "openEvent", "()V"); + g_firebaseAnalytics.m_AddParamNumber = env->GetMethodID(cls, "addParamNumber", "(Ljava/lang/String;D)V"); + g_firebaseAnalytics.m_AddParamInt = env->GetMethodID(cls, "addParamInt", "(Ljava/lang/String;I)V"); + g_firebaseAnalytics.m_AddParamString = env->GetMethodID(cls, "addParamString", "(Ljava/lang/String;Ljava/lang/String;)V"); + g_firebaseAnalytics.m_SendEvent = env->GetMethodID(cls, "sendEvent", "(Ljava/lang/String;)V"); + g_firebaseAnalytics.m_CloseEvent = env->GetMethodID(cls, "closeEvent", "()V"); + } + + void Initialize_Ext() + { + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + jclass cls = dmAndroid::LoadClass(env, "com.defold.firebase.analytics.FirebaseAnalyticsJNI"); + + InitJNIMethods(env, cls); + + jmethodID jni_constructor = env->GetMethodID(cls, "", "(Landroid/app/Activity;)V"); + g_firebaseAnalytics.m_JNI = env->NewGlobalRef(env->NewObject(cls, jni_constructor, threadAttacher.GetActivity()->clazz)); + } + + void Initialize() + { + CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_Initialize); + } + + void GetInstanceId() + { + CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_GetInstanceId); + } + + void SetUserId(const char* id) + { + CallVoidMethodChar(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_SetUserID, id); + } + + void SetUserProperty(const char* name, const char* value) + { + CallVoidMethodCharChar(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_SetUserProperty, name, value); + } + + void ResetAnalyticsData() + { + CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_Reset); + } + + void SetAnalyticsCollectionEnabled(bool enabled) + { + dmAndroid::ThreadAttacher threadAttacher; + JNIEnv* env = threadAttacher.GetEnv(); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_SetEnabled, enabled); + } + + //--- + dmAndroid::ThreadAttacher* g_threadAttacher = 0x0; + + void OpenEvent() + { + if (g_threadAttacher != 0x0) + { + delete g_threadAttacher; + g_threadAttacher = 0x0; + } + g_threadAttacher = new dmAndroid::ThreadAttacher(); + JNIEnv* env = g_threadAttacher->GetEnv(); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI, g_firebaseAnalytics.m_OpenEvent); + } + + void AddParamString(const char* param_name, const char* param) + { + JNIEnv* env = g_threadAttacher->GetEnv(); + jstring jparam_name = env->NewStringUTF(param_name); + jstring jparam = env->NewStringUTF(param); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_AddParamString, jparam_name, jparam); + env->DeleteLocalRef(jparam_name); + env->DeleteLocalRef(jparam); + } + + void AddParamNumber(const char* param_name, double param) + { + JNIEnv* env = g_threadAttacher->GetEnv(); + jstring jparam_name = env->NewStringUTF(param_name); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_AddParamNumber, jparam_name, param); + env->DeleteLocalRef(jparam_name); + } + + void AddParamInt(const char* param_name, int param) + { + JNIEnv* env = g_threadAttacher->GetEnv(); + jstring jparam_name = env->NewStringUTF(param_name); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI , g_firebaseAnalytics.m_AddParamInt, jparam_name, param); + env->DeleteLocalRef(jparam_name); + } + + void SendEvent(const char* event_name) + { + JNIEnv* env = g_threadAttacher->GetEnv(); + jstring jevent_name = env->NewStringUTF(event_name); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI, g_firebaseAnalytics.m_SendEvent, jevent_name); + env->DeleteLocalRef(jevent_name); + } + + void CloseEvent() + { + JNIEnv* env = g_threadAttacher->GetEnv(); + env->CallVoidMethod(g_firebaseAnalytics.m_JNI, g_firebaseAnalytics.m_CloseEvent); + delete g_threadAttacher; + g_threadAttacher = 0x0; + } + +} //namespace dmFirebaseAnalytics + +#endif // DM_PLATFORM_ANDROID diff --git a/firebase_analytics/src/firebase_analytics_callback.cpp b/firebase_analytics/src/firebase_analytics_callback.cpp new file mode 100644 index 0000000..6d6031e --- /dev/null +++ b/firebase_analytics/src/firebase_analytics_callback.cpp @@ -0,0 +1,114 @@ +#if defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) + +#include "firebase_analytics_callback.h" +#include + +namespace dmFirebaseAnalytics { + +static dmScript::LuaCallbackInfo* m_luaCallback = 0x0; +static dmArray m_callbacksQueue; +static dmMutex::HMutex m_mutex; + +static void DestroyCallback() +{ + if (m_luaCallback != 0x0) + { + dmScript::DestroyCallback(m_luaCallback); + m_luaCallback = 0x0; + } +} + +static void InvokeCallback(Message type, const char*json) +{ + if (!dmScript::IsCallbackValid(m_luaCallback)) + { + dmLogError("Firebase callback is invalid. Set new callback unsing `firebase.setCallback()` function."); + return; + } + + lua_State* L = dmScript::GetCallbackLuaContext(m_luaCallback); + int top = lua_gettop(L); + + if (!dmScript::SetupCallback(m_luaCallback)) + { + return; + } + + lua_pushnumber(L, type); + dmScript::JsonToLua(L, json, strlen(json)); + + int number_of_arguments = 3; + int ret = dmScript::PCall(L, number_of_arguments, 0); + (void)ret; + dmScript::TeardownCallback(m_luaCallback); + + assert(top == lua_gettop(L)); +} + +void InitializeCallback() +{ + m_mutex = dmMutex::New(); +} + +void FinalizeCallback() +{ + dmMutex::Delete(m_mutex); + DestroyCallback(); +} + +void SetLuaCallback(lua_State* L, int pos) +{ + int type = lua_type(L, pos); + if (type == LUA_TNONE || type == LUA_TNIL) + { + DestroyCallback(); + } + else + { + m_luaCallback = dmScript::CreateCallback(L, pos); + } +} + +void AddToQueueCallback(Message type, const char*json) +{ + CallbackData data; + data.msg = type; + data.json = json ? strdup(json) : NULL; + + DM_MUTEX_SCOPED_LOCK(m_mutex); + if(m_callbacksQueue.Full()) + { + m_callbacksQueue.OffsetCapacity(2); + } + + m_callbacksQueue.Push(data); +} + +void UpdateCallback() +{ + if (m_callbacksQueue.Empty()) + { + return; + } + + dmArray tmp; + { + DM_MUTEX_SCOPED_LOCK(m_mutex); + tmp.Swap(m_callbacksQueue); + } + + for(uint32_t i = 0; i != tmp.Size(); ++i) + { + CallbackData* data = &tmp[i]; + InvokeCallback(data->msg, data->json); + if(data->json) + { + free(data->json); + data->json = 0; + } + } +} + +} //namespace dmFirebaseAnalytics + +#endif //defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) \ No newline at end of file diff --git a/firebase_analytics/src/firebase_analytics_callback.h b/firebase_analytics/src/firebase_analytics_callback.h new file mode 100644 index 0000000..6ac99d2 --- /dev/null +++ b/firebase_analytics/src/firebase_analytics_callback.h @@ -0,0 +1,30 @@ +#if defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) +#pragma once + +#include + +namespace dmFirebaseAnalytics { + +enum Message +{ + MSG_ERROR = 0, + MSG_INSTANCE_ID = 1, +}; + + +struct CallbackData +{ + Message msg; + char* json; +}; + +void SetLuaCallback(lua_State* L, int pos); +void UpdateCallback(); +void InitializeCallback(); +void FinalizeCallback(); + +void AddToQueueCallback(Message type, const char* json); + +} //namespace dmFirebaseAnalytics + +#endif // defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) diff --git a/firebase_analytics/src/firebase_analytics_private.h b/firebase_analytics/src/firebase_analytics_private.h new file mode 100644 index 0000000..29cd650 --- /dev/null +++ b/firebase_analytics/src/firebase_analytics_private.h @@ -0,0 +1,23 @@ +#if defined(DM_PLATFORM_ANDROID) || defined(DM_PLATFORM_IOS) + +#pragma once + +namespace dmFirebaseAnalytics { + void Initialize_Ext(); + void Initialize(); + void GetInstanceId(); + void SetUserId(const char* id); + void SetUserProperty(const char* name, const char* value); + void ResetAnalyticsData(); + void SetAnalyticsCollectionEnabled(bool enabled); + + void OpenEvent(); + void AddParamString(const char* param_name, const char* param); + void AddParamNumber(const char* param_name, double param); + void AddParamInt(const char* param_name, int param); + void SendEvent(const char* event_name); + void CloseEvent(); + +} //namespace dmFirebaseAnalytics + +#endif diff --git a/firebase_analytics/src/java/com/defold/firebase/analytics/FirebaseAnalyticsJNI.java b/firebase_analytics/src/java/com/defold/firebase/analytics/FirebaseAnalyticsJNI.java new file mode 100644 index 0000000..37038af --- /dev/null +++ b/firebase_analytics/src/java/com/defold/firebase/analytics/FirebaseAnalyticsJNI.java @@ -0,0 +1,153 @@ +package com.defold.firebase.analytics; + +import android.app.Activity; + +import com.google.firebase.analytics.FirebaseAnalytics; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; + +import android.os.Bundle; +import android.util.Log; + +import org.json.JSONObject; +import org.json.JSONException; + +public class FirebaseAnalyticsJNI { + private static final String TAG = "FirebaseAnalyticsJNI"; + + public static native void firebaseAddToQueue(int msg, String json); + + private FirebaseAnalytics firebaseAnalytics; + private Activity activity; + + private static final int MSG_ERROR = 0; + private static final int MSG_INSTANCE_ID = 1; + + FirebaseAnalyticsJNI(Activity activity) { + this.activity = activity; + } + + public void initialize() { + this.firebaseAnalytics = FirebaseAnalytics.getInstance(activity); + } + + public void getInstanceId() { + this.firebaseAnalytics.getAppInstanceId().addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(Task task) { + if (task.isSuccessful()) { + String appInstanceId = task.getResult(); + sendSimpleMessage(MSG_INSTANCE_ID, "instance_id", appInstanceId); + } else { + Exception e = task.getException(); + String errorMessage = e.getMessage(); + sendErrorMessage("Firebase Analytics can't recieve instance id: " + errorMessage); + } + } + }); + } + + public void setUserId(String id) { + firebaseAnalytics.setUserId(id); + } + + public void setUserProperty(String name, String value) { + firebaseAnalytics.setUserProperty(name, value); + } + + public void logEvent(String event_name) { + firebaseAnalytics.logEvent(event_name, null); + } + + public void logEventString(String param_name, String param, String event_name) { + Bundle bundle = new Bundle(); + bundle.putString(param_name, param); + firebaseAnalytics.logEvent(event_name, bundle); + } + + public void logEventInt(String param_name, int param, String event_name) { + Bundle bundle = new Bundle(); + bundle.putInt(param_name, param); + firebaseAnalytics.logEvent(event_name, bundle); + } + + public void logEventNumber(String param_name, double param, String event_name) { + Bundle bundle = new Bundle(); + bundle.putDouble(param_name, param); + firebaseAnalytics.logEvent(event_name, bundle); + } + + public void resetAnalyticsData() { + firebaseAnalytics.resetAnalyticsData(); + } + + public void setAnalyticsCollectionEnabled(boolean enabled) { + firebaseAnalytics.setAnalyticsCollectionEnabled(enabled); + } + + //--- + + private Bundle g_bundle; + + public void openEvent() { + g_bundle = new Bundle(); + } + + public void addParamNumber(String param_name, double param) { + g_bundle.putDouble(param_name, param); + } + + public void addParamInt(String param_name, int param) { + g_bundle.putInt(param_name, param); + } + + public void addParamString(String param_name, String param) { + g_bundle.putString(param_name, param); + } + + public void sendEvent(String event_name) { + firebaseAnalytics.logEvent(event_name, g_bundle); + } + + public void closeEvent() { + g_bundle = null; + } + + //--- + + private String getJsonConversionErrorMessage(String errorText) { + String message = null; + try { + JSONObject obj = new JSONObject(); + obj.put("error", errorText); + message = obj.toString(); + } catch (JSONException e) { + message = "{ \"error\": \"Error while converting simple message to JSON.\"}"; + } + + return message; + } + + private void sendErrorMessage(String errorText) { + String message = getJsonConversionErrorMessage(errorText); + Log.d(TAG, "Analytics Error"); + Log.d(TAG, message); + firebaseAddToQueue(MSG_ERROR, message); + } + + private void sendSimpleMessage(int msg, String key_1, String value_1) { + String message = null; + try { + JSONObject obj = new JSONObject(); + obj.put(key_1, value_1); + message = obj.toString(); + } catch (JSONException e) { + message = getJsonConversionErrorMessage(e.getLocalizedMessage()); + } + firebaseAddToQueue(msg, message); + } + + private void sendSimpleMessage(int msg) { + firebaseAddToQueue(msg, "{}"); + } +} diff --git a/game.project b/game.project index 20ff935..79bf601 100644 --- a/game.project +++ b/game.project @@ -1,6 +1,7 @@ [project] title = extension-firebase-analytics custom_resources = +bundle_resources = /bundle dependencies#0 = https://github.com/britzl/gooey/archive/6.5.0.zip dependencies#1 = https://github.com/britzl/monarch/archive/2.13.0.zip dependencies#2 = https://github.com/defold/extension-firebase/archive/refs/heads/master.zip diff --git a/py_scripts/convert.py b/py_scripts/convert.py new file mode 100644 index 0000000..8b8ca1c --- /dev/null +++ b/py_scripts/convert.py @@ -0,0 +1,25 @@ +import re + +# Define the replacement pattern +pattern = r'lua_pushtablestringstring\(L, "(.*?)", (\w+?)\);' + +# Replacement function +def replace(match): + event_name = match.group(1) + constant_name = match.group(2) + new_event_name = event_name.replace("EVENT_ADDPAYMENTINFO", constant_name.upper()) + new_event_name = event_name.replace("PARAM_ACHIEVEMENTID", constant_name.upper()) + new_event_name = event_name.replace("PROP_ACHIEVEMENTID", constant_name.upper()) + new_event_name = new_event_name.replace("KEVENT", "EVENT_") + new_event_name = new_event_name.replace("KUSERPROPERTY", "PROP_") + return f'lua_pushtablestringstring(L, "{new_event_name}", {constant_name});' + +# Input multiline string +input_string = """ + lua_pushtablestringstring(L, "PROP_ACHIEVEMENTID", kUserPropertyAllowAdPersonalizationSignals); + lua_pushtablestringstring(L, "PROP_ACHIEVEMENTID", kUserPropertySignUpMethod); +""" + +# Perform the conversion using regular expressions +converted_string = re.sub(pattern, replace, input_string) +print(converted_string) diff --git a/py_scripts/doc.py b/py_scripts/doc.py new file mode 100644 index 0000000..c48742b --- /dev/null +++ b/py_scripts/doc.py @@ -0,0 +1,125 @@ +import re + +def parse_multiline_string(input_str): + patterns = re.findall(r'lua_pushtablestringstring\(L, "(.*?)", (.*?);', input_str, re.DOTALL) + + output_lines = [] + for name, value in patterns: + output_lines.append(f' - name: {name}\n' + # f' type: {type(value).__name__}\n' + f' type: string\n' + f' desc: Predefined event\n') + + return '\n'.join(output_lines) + +# Example usage +multiline_input = """ + lua_pushtablestringstring(L, "EVENT_ADIMPRESSION", kEventAdImpression); + lua_pushtablestringstring(L, "EVENT_ADDPAYMENTINFO", kEventAddPaymentInfo); + lua_pushtablestringstring(L, "EVENT_ADDSHIPPINGINFO", kEventAddShippingInfo); + lua_pushtablestringstring(L, "EVENT_ADDTOCART", kEventAddToCart); + lua_pushtablestringstring(L, "EVENT_ADDTOWISHLIST", kEventAddToWishlist); + lua_pushtablestringstring(L, "EVENT_APPOPEN", kEventAppOpen); + lua_pushtablestringstring(L, "EVENT_BEGINCHECKOUT", kEventBeginCheckout); + lua_pushtablestringstring(L, "EVENT_CAMPAIGNDETAILS", kEventCampaignDetails); + lua_pushtablestringstring(L, "EVENT_EARNVIRTUALCURRENCY", kEventEarnVirtualCurrency); + lua_pushtablestringstring(L, "EVENT_GENERATELEAD", kEventGenerateLead); + lua_pushtablestringstring(L, "EVENT_JOINGROUP", kEventJoinGroup); + lua_pushtablestringstring(L, "EVENT_LEVELEND", kEventLevelEnd); + lua_pushtablestringstring(L, "EVENT_LEVELSTART", kEventLevelStart); + lua_pushtablestringstring(L, "EVENT_LEVELUP", kEventLevelUp); + lua_pushtablestringstring(L, "EVENT_LOGIN", kEventLogin); + lua_pushtablestringstring(L, "EVENT_POSTSCORE", kEventPostScore); + lua_pushtablestringstring(L, "EVENT_PURCHASE", kEventPurchase); + lua_pushtablestringstring(L, "EVENT_REFUND", kEventRefund); + lua_pushtablestringstring(L, "EVENT_REMOVEFROMCART", kEventRemoveFromCart); + lua_pushtablestringstring(L, "EVENT_SCREENVIEW", kEventScreenView); + lua_pushtablestringstring(L, "EVENT_SEARCH", kEventSearch); + lua_pushtablestringstring(L, "EVENT_SELECTCONTENT", kEventSelectContent); + lua_pushtablestringstring(L, "EVENT_SELECTITEM", kEventSelectItem); + lua_pushtablestringstring(L, "EVENT_SELECTPROMOTION", kEventSelectPromotion); + lua_pushtablestringstring(L, "EVENT_SHARE", kEventShare); + lua_pushtablestringstring(L, "EVENT_SIGNUP", kEventSignUp); + lua_pushtablestringstring(L, "EVENT_SPENDVIRTUALCURRENCY", kEventSpendVirtualCurrency); + lua_pushtablestringstring(L, "EVENT_TUTORIALBEGIN", kEventTutorialBegin); + lua_pushtablestringstring(L, "EVENT_TUTORIALCOMPLETE", kEventTutorialComplete); + lua_pushtablestringstring(L, "EVENT_UNLOCKACHIEVEMENT", kEventUnlockAchievement); + lua_pushtablestringstring(L, "EVENT_VIEWCART", kEventViewCart); + lua_pushtablestringstring(L, "EVENT_VIEWITEM", kEventViewItem); + lua_pushtablestringstring(L, "EVENT_VIEWITEMLIST", kEventViewItemList); + lua_pushtablestringstring(L, "EVENT_VIEWPROMOTION", kEventViewPromotion); + lua_pushtablestringstring(L, "EVENT_VIEWSEARCHRESULTS", kEventViewSearchResults); + lua_pushtablestringstring(L, "PARAM_ADFORMAT", kParameterAdFormat); + lua_pushtablestringstring(L, "PARAM_ADNETWORKCLICKID", kParameterAdNetworkClickID); + lua_pushtablestringstring(L, "PARAM_ADPLATFORM", kParameterAdPlatform); + lua_pushtablestringstring(L, "PARAM_ADSOURCE", kParameterAdSource); + lua_pushtablestringstring(L, "PARAM_ADUNITNAME", kParameterAdUnitName); + lua_pushtablestringstring(L, "PARAM_AFFILIATION", kParameterAffiliation); + lua_pushtablestringstring(L, "PARAM_CP1", kParameterCP1); + lua_pushtablestringstring(L, "PARAM_CAMPAIGN", kParameterCampaign); + lua_pushtablestringstring(L, "PARAM_CAMPAIGNID", kParameterCampaignID); + lua_pushtablestringstring(L, "PARAM_CHARACTER", kParameterCharacter); + lua_pushtablestringstring(L, "PARAM_CONTENT", kParameterContent); + lua_pushtablestringstring(L, "PARAM_CONTENTTYPE", kParameterContentType); + lua_pushtablestringstring(L, "PARAM_COUPON", kParameterCoupon); + lua_pushtablestringstring(L, "PARAM_CREATIVEFORMAT", kParameterCreativeFormat); + lua_pushtablestringstring(L, "PARAM_CREATIVENAME", kParameterCreativeName); + lua_pushtablestringstring(L, "PARAM_CREATIVESLOT", kParameterCreativeSlot); + lua_pushtablestringstring(L, "PARAM_CURRENCY", kParameterCurrency); + lua_pushtablestringstring(L, "PARAM_DESTINATION", kParameterDestination); + lua_pushtablestringstring(L, "PARAM_DISCOUNT", kParameterDiscount); + lua_pushtablestringstring(L, "PARAM_ENDDATE", kParameterEndDate); + lua_pushtablestringstring(L, "PARAM_EXTENDSESSION", kParameterExtendSession); + lua_pushtablestringstring(L, "PARAM_FLIGHTNUMBER", kParameterFlightNumber); + lua_pushtablestringstring(L, "PARAM_GROUPID", kParameterGroupID); + lua_pushtablestringstring(L, "PARAM_INDEX", kParameterIndex); + lua_pushtablestringstring(L, "PARAM_ITEMBRAND", kParameterItemBrand); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY", kParameterItemCategory); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY2", kParameterItemCategory2); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY3", kParameterItemCategory3); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY4", kParameterItemCategory4); + lua_pushtablestringstring(L, "PARAM_ITEMCATEGORY5", kParameterItemCategory5); + lua_pushtablestringstring(L, "PARAM_ITEMID", kParameterItemID); + lua_pushtablestringstring(L, "PARAM_ITEMLISTID", kParameterItemListID); + lua_pushtablestringstring(L, "PARAM_ITEMLISTNAME", kParameterItemListName); + lua_pushtablestringstring(L, "PARAM_ITEMNAME", kParameterItemName); + lua_pushtablestringstring(L, "PARAM_ITEMVARIANT", kParameterItemVariant); + lua_pushtablestringstring(L, "PARAM_ITEMS", kParameterItems); + lua_pushtablestringstring(L, "PARAM_LEVEL", kParameterLevel); + lua_pushtablestringstring(L, "PARAM_LEVELNAME", kParameterLevelName); + lua_pushtablestringstring(L, "PARAM_LOCATION", kParameterLocation); + lua_pushtablestringstring(L, "PARAM_LOCATIONID", kParameterLocationID); + lua_pushtablestringstring(L, "PARAM_MARKETINGTACTIC", kParameterMarketingTactic); + lua_pushtablestringstring(L, "PARAM_MEDIUM", kParameterMedium); + lua_pushtablestringstring(L, "PARAM_METHOD", kParameterMethod); + lua_pushtablestringstring(L, "PARAM_NUMBEROFNIGHTS", kParameterNumberOfNights); + lua_pushtablestringstring(L, "PARAM_NUMBEROFPASSENGERS", kParameterNumberOfPassengers); + lua_pushtablestringstring(L, "PARAM_NUMBEROFROOMS", kParameterNumberOfRooms); + lua_pushtablestringstring(L, "PARAM_ORIGIN", kParameterOrigin); + lua_pushtablestringstring(L, "PARAM_PAYMENTTYPE", kParameterPaymentType); + lua_pushtablestringstring(L, "PARAM_PRICE", kParameterPrice); + lua_pushtablestringstring(L, "PARAM_PROMOTIONID", kParameterPromotionID); + lua_pushtablestringstring(L, "PARAM_PROMOTIONNAME", kParameterPromotionName); + lua_pushtablestringstring(L, "PARAM_QUANTITY", kParameterQuantity); + lua_pushtablestringstring(L, "PARAM_SCORE", kParameterScore); + lua_pushtablestringstring(L, "PARAM_SCREENCLASS", kParameterScreenClass); + lua_pushtablestringstring(L, "PARAM_SCREENNAME", kParameterScreenName); + lua_pushtablestringstring(L, "PARAM_SEARCHTERM", kParameterSearchTerm); + lua_pushtablestringstring(L, "PARAM_SHIPPING", kParameterShipping); + lua_pushtablestringstring(L, "PARAM_SHIPPINGTIER", kParameterShippingTier); + lua_pushtablestringstring(L, "PARAM_SOURCE", kParameterSource); + lua_pushtablestringstring(L, "PARAM_SOURCEPLATFORM", kParameterSourcePlatform); + lua_pushtablestringstring(L, "PARAM_STARTDATE", kParameterStartDate); + lua_pushtablestringstring(L, "PARAM_SUCCESS", kParameterSuccess); + lua_pushtablestringstring(L, "PARAM_TAX", kParameterTax); + lua_pushtablestringstring(L, "PARAM_TERM", kParameterTerm); + lua_pushtablestringstring(L, "PARAM_TRANSACTIONID", kParameterTransactionID); + lua_pushtablestringstring(L, "PARAM_TRAVELCLASS", kParameterTravelClass); + lua_pushtablestringstring(L, "PARAM_VALUE", kParameterValue); + lua_pushtablestringstring(L, "PARAM_VIRTUALCURRENCYNAME", kParameterVirtualCurrencyName); + lua_pushtablestringstring(L, "PROP_ALLOWADPERSONALIZATIONSIGNALS", kUserPropertyAllowAdPersonalizationSignals); + lua_pushtablestringstring(L, "PROP_SIGNUPMETHOD", kUserPropertySignUpMethod); +""" + +output = parse_multiline_string(multiline_input) +print(output) \ No newline at end of file