Skip to content

Commit 2a39fc0

Browse files
authored
Merge pull request #44 from OutSystems/development
RMET-3677 & RMET-3678 - Add setConsent functionality
2 parents 36e273b + a531d14 commit 2a39fc0

9 files changed

Lines changed: 213 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
The changes documented here do not include those from the original repository.
88

9+
## 5.0.0-OS15
10+
11+
- Feat(Android & iOS): Add setConsent functionality to allow users to set consent for analytics (https://outsystemsrd.atlassian.net/browse/RMET-3677 & https://outsystemsrd.atlassian.net/browse/RMET-3678).
12+
913
## 5.0.0-OS14
1014

1115
- Android | Update dependency to Firebase Analytics Android library (https://outsystemsrd.atlassian.net/browse/RMET-3608).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cordova-plugin-firebase-analytics",
3-
"version": "5.0.0-OS14",
3+
"version": "5.0.0-OS15",
44
"description": "Cordova plugin for Firebase Analytics",
55
"cordova": {
66
"id": "cordova-plugin-firebase-analytics",

plugin.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
22
xmlns:android="http://schemas.android.com/apk/res/android"
33
id="cordova-plugin-firebase-analytics"
4-
version="5.0.0-OS14">
4+
version="5.0.0-OS15">
55

66
<name>FirebaseAnalyticsPlugin</name>
77
<description>Cordova plugin for Firebase Analytics</description>
@@ -57,6 +57,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
5757
<source-file src="src/ios/Common/OSFANLDefaultValues.swift" target-dir="Common" />
5858
<source-file src="src/ios/Common/OSFANLError.swift" target-dir="Common" />
5959
<source-file src="src/ios/Common/OSFANLInputDataFieldKey.swift" target-dir="Common" />
60+
<source-file src="src/ios/Common/OSFANLConsentHelper.swift" target-dir="Common" />
6061

6162
<source-file src="src/ios/InputTransformer/OSFANLInputTransformable.swift" target-dir="InputTransformer" />
6263
<source-file src="src/ios/InputTransformer/OSFANLInputTransformableModel.swift" target-dir="InputTransformer" />
@@ -108,6 +109,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
108109
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLBundle+putAny.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
109110
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLJSONArray+extension.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
110111
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLDefaultValues.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
112+
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLConsentModels.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
111113
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLError.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
112114
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLEventOutputModel.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
113115
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLInputDataFieldKey.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />

src/android/com/outsystems/firebase/analytics/FirebaseAnalyticsPlugin.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99

1010
import com.google.firebase.analytics.FirebaseAnalytics;
1111
import com.outsystems.firebase.analytics.OSFANLManager;
12+
import com.outsystems.firebase.analytics.model.ConsentType;
13+
import com.outsystems.firebase.analytics.model.ConsentStatus;
1214
import com.outsystems.firebase.analytics.model.OSFANLError;
1315
import com.outsystems.firebase.analytics.model.OSFANLEventOutputModel;
1416

1517
import org.apache.cordova.CallbackContext;
18+
import org.json.JSONArray;
1619
import org.json.JSONException;
1720
import org.json.JSONObject;
1821

1922
import java.util.Iterator;
23+
import java.util.HashMap;
24+
import java.util.Map;
2025

2126

2227
public class FirebaseAnalyticsPlugin extends ReflectiveCordovaPlugin {
@@ -103,6 +108,50 @@ private void logECommerceEvent(JSONObject params, CallbackContext callbackContex
103108
}
104109
}
105110

111+
@CordovaMethod
112+
private void setConsent(String consentSetting, CallbackContext callbackContext) throws JSONException {
113+
114+
try {
115+
JSONArray consentSettings = new JSONArray(consentSetting);
116+
117+
Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new HashMap<>();
118+
119+
for (int i = 0; i < consentSettings.length(); i++) {
120+
JSONObject consentItem = consentSettings.getJSONObject(i);
121+
int typeValue = consentItem.getInt("Type");
122+
int statusValue = consentItem.getInt("Status");
123+
124+
FirebaseAnalytics.ConsentType consentType = ConsentType.fromInt(typeValue);
125+
FirebaseAnalytics.ConsentStatus consentStatus = ConsentStatus.fromInt(statusValue);
126+
127+
if (consentType != null) {
128+
if (consentStatus != null) {
129+
if (consentMap.containsKey(consentType)) {
130+
throw OSFANLError.Companion.duplicateItemsIn("ConsentSettings");
131+
}
132+
consentMap.put(consentType, consentStatus);
133+
} else {
134+
throw OSFANLError.Companion.invalidType("Consent Status of " + consentType, "GRANTED, or DENIED");
135+
}
136+
} else {
137+
throw OSFANLError.Companion.invalidType("Consent Type", "AD_PERSONALIZATION, AD_STORAGE, AD_USER_DATA, or ANALYTICS_STORAGE");
138+
}
139+
}
140+
141+
if (!consentMap.isEmpty()) {
142+
this.firebaseAnalytics.setConsent(consentMap);
143+
callbackContext.success();
144+
} else {
145+
throw OSFANLError.Companion.missing("ConsentSettings");
146+
}
147+
} catch (OSFANLError e) {
148+
JSONObject result = new JSONObject();
149+
result.put("code", e.getCode());
150+
result.put("message", e.getMessage());
151+
callbackContext.error(result);
152+
}
153+
}
154+
106155
private static Bundle parse(JSONObject params) throws JSONException {
107156
Bundle bundle = new Bundle();
108157
Iterator<String> it = params.keys();
@@ -126,4 +175,4 @@ private static Bundle parse(JSONObject params) throws JSONException {
126175

127176
return bundle;
128177
}
129-
}
178+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.outsystems.firebase.analytics.model
2+
3+
import com.google.firebase.analytics.FirebaseAnalytics
4+
5+
enum class ConsentType(val value: Int, val consentType: FirebaseAnalytics.ConsentType) {
6+
AD_PERSONALIZATION(1, FirebaseAnalytics.ConsentType.AD_PERSONALIZATION),
7+
AD_STORAGE(2, FirebaseAnalytics.ConsentType.AD_STORAGE),
8+
AD_USER_DATA(3, FirebaseAnalytics.ConsentType.AD_USER_DATA),
9+
ANALYTICS_STORAGE(4, FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE);
10+
11+
companion object {
12+
private val map = entries.associateBy(ConsentType::value)
13+
14+
@JvmStatic
15+
fun fromInt(value: Int): FirebaseAnalytics.ConsentType? = map[value]?.consentType
16+
}
17+
}
18+
19+
enum class ConsentStatus(val value: Int, val consentStatus: FirebaseAnalytics.ConsentStatus) {
20+
GRANTED(1, FirebaseAnalytics.ConsentStatus.GRANTED),
21+
DENIED(2, FirebaseAnalytics.ConsentStatus.DENIED);
22+
23+
companion object {
24+
private val map = entries.associateBy(ConsentStatus::value)
25+
26+
@JvmStatic
27+
fun fromInt(value: Int): FirebaseAnalytics.ConsentStatus? = map[value]?.consentStatus
28+
}
29+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import FirebaseAnalytics
2+
import FirebaseCore
3+
4+
@objc enum ConsentTypeRawValue: Int, CustomStringConvertible, CaseIterable {
5+
case adPersonalization = 1
6+
case adStorage = 2
7+
case adUserData = 3
8+
case analyticsStorage = 4
9+
10+
var description: String {
11+
return switch self {
12+
case .adPersonalization: "ad_personalization"
13+
case .adStorage: "ad_storage"
14+
case .adUserData: "ad_user_data"
15+
case .analyticsStorage: "analytics_storage"
16+
}
17+
}
18+
19+
static func allOptionsString() -> String {
20+
let capitalizedDescriptions = allCases.map { $0.description.uppercased() }
21+
22+
if capitalizedDescriptions.count > 1 {
23+
let lastOption = capitalizedDescriptions.last!
24+
let allButLast = capitalizedDescriptions.dropLast().joined(separator: ", ")
25+
return "\(allButLast), or \(lastOption)"
26+
} else {
27+
return capitalizedDescriptions.first ?? ""
28+
}
29+
}
30+
}
31+
32+
@objc enum ConsentStatusRawValue: Int, CustomStringConvertible, CaseIterable {
33+
case granted = 1
34+
case denied = 2
35+
36+
var description: String {
37+
return switch self {
38+
case .granted: "granted"
39+
case .denied: "denied"
40+
}
41+
}
42+
43+
static func allOptionsString() -> String {
44+
let capitalizedDescriptions = allCases.map { $0.description.uppercased() }
45+
46+
if capitalizedDescriptions.count > 1 {
47+
let lastOption = capitalizedDescriptions.last!
48+
let allButLast = capitalizedDescriptions.dropLast().joined(separator: ", ")
49+
return "\(allButLast), or \(lastOption)"
50+
} else {
51+
return capitalizedDescriptions.first ?? ""
52+
}
53+
}
54+
}
55+
56+
@objc class OSFANLConsentHelper: NSObject {
57+
@objc static func createConsentModel(_ commandArguments: NSArray) throws -> [ConsentType: ConsentStatus] {
58+
guard let jsonString = commandArguments[0] as? String,
59+
let jsonData = jsonString.data(using: .utf8),
60+
let array = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String: Any]] else {
61+
throw OSFANLError.invalidType("ConsentSettings", type: "JSON")
62+
}
63+
64+
var firebaseConsentDict: [ConsentType: ConsentStatus] = [:]
65+
66+
for item in array {
67+
guard let typeRawValue = item["Type"] as? Int,
68+
let statusRawValue = item["Status"] as? Int else {
69+
throw OSFANLError.invalidType("JSON passed Consent Type or Status", type: "Integer")
70+
}
71+
72+
guard let consentTypeRawValue = ConsentTypeRawValue(rawValue: typeRawValue) else {
73+
throw OSFANLError.invalidType("Consent Type", type: ConsentTypeRawValue.allOptionsString())
74+
}
75+
76+
guard let consentStatusRawValue = ConsentStatusRawValue(rawValue: statusRawValue) else {
77+
throw OSFANLError.invalidType("Consent Status", type: ConsentStatusRawValue.allOptionsString())
78+
}
79+
80+
let consentType = ConsentType(rawValue: String(describing: consentTypeRawValue))
81+
let consentStatus = ConsentStatus(rawValue: String(describing: consentStatusRawValue))
82+
83+
if firebaseConsentDict.keys.contains(consentType) {
84+
throw OSFANLError.duplicateItemsIn(parameter: "ConsentSettings")
85+
} else {
86+
firebaseConsentDict[consentType] = consentStatus
87+
}
88+
}
89+
90+
if firebaseConsentDict.isEmpty {
91+
throw OSFANLError.missing("ConsentSettings")
92+
} else {
93+
return firebaseConsentDict
94+
}
95+
}
96+
}

src/ios/FirebaseAnalyticsPlugin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@
1111
- (void)resetAnalyticsData:(CDVInvokedUrlCommand*)command;
1212
- (void)setDefaultEventParameters:(CDVInvokedUrlCommand*)command;
1313
- (void)requestTrackingAuthorization:(CDVInvokedUrlCommand*)command;
14+
- (void)setConsent:(CDVInvokedUrlCommand*)command;
1415

1516
@end

src/ios/FirebaseAnalyticsPlugin.m

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ - (void)showTrackingAuthorizationPopup:(CDVInvokedUrlCommand *)command {
143143
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
144144
}
145145

146+
- (void)setConsent:(CDVInvokedUrlCommand*)command
147+
{
148+
NSError *error;
149+
NSDictionary *consentModel = [OSFANLConsentHelper createConsentModel:command.arguments error:&error];
150+
if (error) {
151+
[self sendError:error forCallbackId:command.callbackId];
152+
} else {
153+
[FIRAnalytics setConsent:consentModel];
154+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
155+
}
156+
}
157+
146158
typedef void (^showPermissionInformationPopupHandler)(UIAlertAction*);
147159
- (void)showPermissionInformationPopup:
148160
(NSString *)title :
@@ -177,5 +189,4 @@ - (void)sendError:(NSError *)error forCallbackId:(NSString *)callbackId {
177189
[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
178190
}
179191

180-
181192
@end

www/FirebaseAnalytics.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,22 @@ module.exports = {
5555
logECommerceEvent: function(event, eventParameters, items, success, error) {
5656
let args = [{event, eventParameters, items}];
5757
exec(success, error, PLUGIN_NAME, 'logECommerceEvent', args);
58+
},
59+
/**
60+
* setConsent
61+
*
62+
* @param {string} consentSettings - A JSON string of an object containing consent settings.
63+
* @param {function} [success] - Success callback function.
64+
* @param {function} [error] - Error callback function.
65+
*
66+
* @example
67+
* const consentSettings = {
68+
* AD_STORAGE: 'GRANTED',
69+
* ANALYTICS_STORAGE: 'GRANTED',
70+
* };
71+
* FirebaseAnalytics.setConsent(JSON.stringify(consentSettings));
72+
*/
73+
setConsent: function (consentSettings, success, error) {
74+
exec(success, error, PLUGIN_NAME, 'setConsent', [consentSettings]);
5875
}
5976
};

0 commit comments

Comments
 (0)