Skip to content

Commit

Permalink
Merge pull request #44 from OutSystems/development
Browse files Browse the repository at this point in the history
RMET-3677 & RMET-3678 - Add setConsent functionality
  • Loading branch information
IT-MikeS authored Oct 10, 2024
2 parents 36e273b + a531d14 commit 2a39fc0
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

## 5.0.0-OS15

- 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).

## 5.0.0-OS14

- Android | Update dependency to Firebase Analytics Android library (https://outsystemsrd.atlassian.net/browse/RMET-3608).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-firebase-analytics",
"version": "5.0.0-OS14",
"version": "5.0.0-OS15",
"description": "Cordova plugin for Firebase Analytics",
"cordova": {
"id": "cordova-plugin-firebase-analytics",
Expand Down
4 changes: 3 additions & 1 deletion plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-firebase-analytics"
version="5.0.0-OS14">
version="5.0.0-OS15">

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

<source-file src="src/ios/InputTransformer/OSFANLInputTransformable.swift" target-dir="InputTransformer" />
<source-file src="src/ios/InputTransformer/OSFANLInputTransformableModel.swift" target-dir="InputTransformer" />
Expand Down Expand Up @@ -108,6 +109,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLBundle+putAny.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLJSONArray+extension.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLDefaultValues.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLConsentModels.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLError.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLEventOutputModel.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
<source-file src="src/android/com/outsystems/firebase/analytics/model/OSFANLInputDataFieldKey.kt" target-dir="app/src/main/kotlin/com/outsystems/firebase/analytics/model/" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@

import com.google.firebase.analytics.FirebaseAnalytics;
import com.outsystems.firebase.analytics.OSFANLManager;
import com.outsystems.firebase.analytics.model.ConsentType;
import com.outsystems.firebase.analytics.model.ConsentStatus;
import com.outsystems.firebase.analytics.model.OSFANLError;
import com.outsystems.firebase.analytics.model.OSFANLEventOutputModel;

import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;


public class FirebaseAnalyticsPlugin extends ReflectiveCordovaPlugin {
Expand Down Expand Up @@ -103,6 +108,50 @@ private void logECommerceEvent(JSONObject params, CallbackContext callbackContex
}
}

@CordovaMethod
private void setConsent(String consentSetting, CallbackContext callbackContext) throws JSONException {

try {
JSONArray consentSettings = new JSONArray(consentSetting);

Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new HashMap<>();

for (int i = 0; i < consentSettings.length(); i++) {
JSONObject consentItem = consentSettings.getJSONObject(i);
int typeValue = consentItem.getInt("Type");
int statusValue = consentItem.getInt("Status");

FirebaseAnalytics.ConsentType consentType = ConsentType.fromInt(typeValue);
FirebaseAnalytics.ConsentStatus consentStatus = ConsentStatus.fromInt(statusValue);

if (consentType != null) {
if (consentStatus != null) {
if (consentMap.containsKey(consentType)) {
throw OSFANLError.Companion.duplicateItemsIn("ConsentSettings");
}
consentMap.put(consentType, consentStatus);
} else {
throw OSFANLError.Companion.invalidType("Consent Status of " + consentType, "GRANTED, or DENIED");
}
} else {
throw OSFANLError.Companion.invalidType("Consent Type", "AD_PERSONALIZATION, AD_STORAGE, AD_USER_DATA, or ANALYTICS_STORAGE");
}
}

if (!consentMap.isEmpty()) {
this.firebaseAnalytics.setConsent(consentMap);
callbackContext.success();
} else {
throw OSFANLError.Companion.missing("ConsentSettings");
}
} catch (OSFANLError e) {
JSONObject result = new JSONObject();
result.put("code", e.getCode());
result.put("message", e.getMessage());
callbackContext.error(result);
}
}

private static Bundle parse(JSONObject params) throws JSONException {
Bundle bundle = new Bundle();
Iterator<String> it = params.keys();
Expand All @@ -126,4 +175,4 @@ private static Bundle parse(JSONObject params) throws JSONException {

return bundle;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.outsystems.firebase.analytics.model

import com.google.firebase.analytics.FirebaseAnalytics

enum class ConsentType(val value: Int, val consentType: FirebaseAnalytics.ConsentType) {
AD_PERSONALIZATION(1, FirebaseAnalytics.ConsentType.AD_PERSONALIZATION),
AD_STORAGE(2, FirebaseAnalytics.ConsentType.AD_STORAGE),
AD_USER_DATA(3, FirebaseAnalytics.ConsentType.AD_USER_DATA),
ANALYTICS_STORAGE(4, FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE);

companion object {
private val map = entries.associateBy(ConsentType::value)

@JvmStatic
fun fromInt(value: Int): FirebaseAnalytics.ConsentType? = map[value]?.consentType
}
}

enum class ConsentStatus(val value: Int, val consentStatus: FirebaseAnalytics.ConsentStatus) {
GRANTED(1, FirebaseAnalytics.ConsentStatus.GRANTED),
DENIED(2, FirebaseAnalytics.ConsentStatus.DENIED);

companion object {
private val map = entries.associateBy(ConsentStatus::value)

@JvmStatic
fun fromInt(value: Int): FirebaseAnalytics.ConsentStatus? = map[value]?.consentStatus
}
}
96 changes: 96 additions & 0 deletions src/ios/Common/OSFANLConsentHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import FirebaseAnalytics
import FirebaseCore

@objc enum ConsentTypeRawValue: Int, CustomStringConvertible, CaseIterable {
case adPersonalization = 1
case adStorage = 2
case adUserData = 3
case analyticsStorage = 4

var description: String {
return switch self {
case .adPersonalization: "ad_personalization"
case .adStorage: "ad_storage"
case .adUserData: "ad_user_data"
case .analyticsStorage: "analytics_storage"
}
}

static func allOptionsString() -> String {
let capitalizedDescriptions = allCases.map { $0.description.uppercased() }

if capitalizedDescriptions.count > 1 {
let lastOption = capitalizedDescriptions.last!
let allButLast = capitalizedDescriptions.dropLast().joined(separator: ", ")
return "\(allButLast), or \(lastOption)"
} else {
return capitalizedDescriptions.first ?? ""
}
}
}

@objc enum ConsentStatusRawValue: Int, CustomStringConvertible, CaseIterable {
case granted = 1
case denied = 2

var description: String {
return switch self {
case .granted: "granted"
case .denied: "denied"
}
}

static func allOptionsString() -> String {
let capitalizedDescriptions = allCases.map { $0.description.uppercased() }

if capitalizedDescriptions.count > 1 {
let lastOption = capitalizedDescriptions.last!
let allButLast = capitalizedDescriptions.dropLast().joined(separator: ", ")
return "\(allButLast), or \(lastOption)"
} else {
return capitalizedDescriptions.first ?? ""
}
}
}

@objc class OSFANLConsentHelper: NSObject {
@objc static func createConsentModel(_ commandArguments: NSArray) throws -> [ConsentType: ConsentStatus] {
guard let jsonString = commandArguments[0] as? String,
let jsonData = jsonString.data(using: .utf8),
let array = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String: Any]] else {
throw OSFANLError.invalidType("ConsentSettings", type: "JSON")
}

var firebaseConsentDict: [ConsentType: ConsentStatus] = [:]

for item in array {
guard let typeRawValue = item["Type"] as? Int,
let statusRawValue = item["Status"] as? Int else {
throw OSFANLError.invalidType("JSON passed Consent Type or Status", type: "Integer")
}

guard let consentTypeRawValue = ConsentTypeRawValue(rawValue: typeRawValue) else {
throw OSFANLError.invalidType("Consent Type", type: ConsentTypeRawValue.allOptionsString())
}

guard let consentStatusRawValue = ConsentStatusRawValue(rawValue: statusRawValue) else {
throw OSFANLError.invalidType("Consent Status", type: ConsentStatusRawValue.allOptionsString())
}

let consentType = ConsentType(rawValue: String(describing: consentTypeRawValue))
let consentStatus = ConsentStatus(rawValue: String(describing: consentStatusRawValue))

if firebaseConsentDict.keys.contains(consentType) {
throw OSFANLError.duplicateItemsIn(parameter: "ConsentSettings")
} else {
firebaseConsentDict[consentType] = consentStatus
}
}

if firebaseConsentDict.isEmpty {
throw OSFANLError.missing("ConsentSettings")
} else {
return firebaseConsentDict
}
}
}
1 change: 1 addition & 0 deletions src/ios/FirebaseAnalyticsPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
- (void)resetAnalyticsData:(CDVInvokedUrlCommand*)command;
- (void)setDefaultEventParameters:(CDVInvokedUrlCommand*)command;
- (void)requestTrackingAuthorization:(CDVInvokedUrlCommand*)command;
- (void)setConsent:(CDVInvokedUrlCommand*)command;

@end
13 changes: 12 additions & 1 deletion src/ios/FirebaseAnalyticsPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ - (void)showTrackingAuthorizationPopup:(CDVInvokedUrlCommand *)command {
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)setConsent:(CDVInvokedUrlCommand*)command
{
NSError *error;
NSDictionary *consentModel = [OSFANLConsentHelper createConsentModel:command.arguments error:&error];
if (error) {
[self sendError:error forCallbackId:command.callbackId];
} else {
[FIRAnalytics setConsent:consentModel];
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
}
}

typedef void (^showPermissionInformationPopupHandler)(UIAlertAction*);
- (void)showPermissionInformationPopup:
(NSString *)title :
Expand Down Expand Up @@ -177,5 +189,4 @@ - (void)sendError:(NSError *)error forCallbackId:(NSString *)callbackId {
[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}


@end
17 changes: 17 additions & 0 deletions www/FirebaseAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,22 @@ module.exports = {
logECommerceEvent: function(event, eventParameters, items, success, error) {
let args = [{event, eventParameters, items}];
exec(success, error, PLUGIN_NAME, 'logECommerceEvent', args);
},
/**
* setConsent
*
* @param {string} consentSettings - A JSON string of an object containing consent settings.
* @param {function} [success] - Success callback function.
* @param {function} [error] - Error callback function.
*
* @example
* const consentSettings = {
* AD_STORAGE: 'GRANTED',
* ANALYTICS_STORAGE: 'GRANTED',
* };
* FirebaseAnalytics.setConsent(JSON.stringify(consentSettings));
*/
setConsent: function (consentSettings, success, error) {
exec(success, error, PLUGIN_NAME, 'setConsent', [consentSettings]);
}
};

0 comments on commit 2a39fc0

Please sign in to comment.