-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathSingularKit.kt
More file actions
433 lines (378 loc) · 15.3 KB
/
SingularKit.kt
File metadata and controls
433 lines (378 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
package com.mparticle.kits
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.mparticle.AttributionResult
import com.mparticle.MPEvent
import com.mparticle.MParticle
import com.mparticle.MParticle.IdentityType
import com.mparticle.commerce.CommerceEvent
import com.mparticle.commerce.Product
import com.mparticle.consent.ConsentState
import com.mparticle.internal.Logger
import com.mparticle.internal.MPUtility
import com.mparticle.kits.KitIntegration.ActivityListener
import com.mparticle.kits.KitIntegration.ApplicationStateListener
import com.mparticle.kits.KitIntegration.AttributeListener
import com.mparticle.kits.KitIntegration.CommerceListener
import com.mparticle.kits.KitIntegration.EventListener
import com.mparticle.kits.KitIntegration.PushListener
import com.mparticle.kits.KitIntegration.UserAttributeListener
import com.singular.sdk.Singular
import com.singular.sdk.SingularConfig
import com.singular.sdk.internal.SingularLog
import org.json.JSONException
import org.json.JSONObject
import java.math.BigDecimal
open class SingularKit : KitIntegration(), ActivityListener, EventListener,
PushListener, CommerceListener, ApplicationStateListener, UserAttributeListener,
AttributeListener {
private val logger = SingularLog.getLogger(Singular::class.java.simpleName)
private var isInitialized = false
private var deviceToken: String? = null
//endregion
//region Kit Integration Implementation
override fun onKitCreate(
settings: Map<String, String>,
context: Context
): List<ReportingMessage> {
// Returning the reporting message to state that the method was successful and
// Preventing from the mParticle Kit to retry to activate to method.
val messages: MutableList<ReportingMessage> = ArrayList()
messages.add(
ReportingMessage(
this,
ReportingMessage.MessageType.APP_STATE_TRANSITION,
System.currentTimeMillis(), null
)
)
return messages
}
fun buildSingularConfig(settings: Map<String, String>?): SingularConfig? {
return try {
val singularKey = settings?.get(API_KEY)
val singularSecret = settings?.get(API_SECRET)
// Getting the DDL timeout from the settings. If does not exist, use 60(S) as default.
val ddlTimeout = settings?.get(DDL_TIMEOUT)
var ddlHandlerTimeoutSec = 60L
if (!KitUtils.isEmpty(ddlTimeout)) {
try {
if (ddlTimeout != null) {
ddlHandlerTimeoutSec = ddlTimeout.toLong()
}
} catch (unableToGetDDLTimeout: Exception) {
}
}
val config = SingularConfig(singularKey, singularSecret)
config.withDDLTimeoutInSec(ddlHandlerTimeoutSec)
val activity = currentActivity.get()
if (activity != null) {
val intent = activity.intent
config.withSingularLink(intent) { singularLinkParams ->
val attributionResult = AttributionResult()
attributionResult.serviceProviderId = MParticle.ServiceProviders.SINGULAR
attributionResult.link = singularLinkParams.deeplink
try {
val linkParams = JSONObject()
linkParams.put(PASSTHROUGH, singularLinkParams.passthrough)
linkParams.put(IS_DEFERRED, singularLinkParams.isDeferred)
attributionResult.parameters = linkParams
} catch (e: JSONException) {
e.printStackTrace()
}
kitManager.onResult(attributionResult)
}
}
// If the environment is in development mode, enable logging.
if (MPUtility.isDevEnv()) {
config.withLoggingEnabled()
config.withLogLevel(Log.DEBUG)
}
Singular.setWrapperNameAndVersion(MPARTICLE_WRAPPER_NAME, MPARTICLE_WRAPPER_VERSION)
config
} catch (ex: Exception) {
logger.error(CANT_BUILD_SINGULAR_CONFIG_MESSAGE, ex)
null
}
}
override fun setOptOut(b: Boolean): List<ReportingMessage> = emptyList()
override fun getName(): String {
return KIT_NAME
}
override fun setInstallReferrer(intent: Intent) {}
//endregion
//region Activity Listener Implementation
override fun onActivityResumed(activity: Activity): List<ReportingMessage> {
Singular.onActivityResumed()
return emptyList()
}
override fun onActivityPaused(activity: Activity): List<ReportingMessage> {
Singular.onActivityPaused()
return emptyList()
}
//region Unimplemented (Empty Methods)
override fun onActivityCreated(activity: Activity, bundle: Bundle?): List<ReportingMessage> {
initializeSingular()
return emptyList()
}
override fun onActivityStarted(activity: Activity): List<ReportingMessage> = emptyList()
override fun onActivityStopped(activity: Activity): List<ReportingMessage> = emptyList()
override fun onActivitySaveInstanceState(
activity: Activity,
bundle: Bundle?
): List<ReportingMessage> = emptyList()
override fun onActivityDestroyed(activity: Activity): List<ReportingMessage> = emptyList()
//endregion
//endregion
//region Event Listener Implementation
override fun logEvent(mpEvent: MPEvent): List<ReportingMessage>? {
val messages: MutableList<ReportingMessage> = ArrayList()
executeIfSingularInitialized({
val eventName = mpEvent.eventName
val eventInfo = mpEvent.customAttributes
// Logging the event with the Singular API
val eventStatus: Boolean = if (!eventInfo.isNullOrEmpty()) {
Singular.eventJSON(eventName, JSONObject(eventInfo))
} else {
Singular.event(eventName)
}
// If the Singular event logging was successful, return the message to the mParticle Kit
// So it won't retry the event
if (eventStatus) {
messages.add(ReportingMessage.fromEvent(this, mpEvent))
}
}, forceInitSingular = true, "logEvent")
return messages
}
//region Unimplemented (Empty Methods)
override fun leaveBreadcrumb(s: String): List<ReportingMessage> = emptyList()
override fun logError(s: String, map: Map<String, String>): List<ReportingMessage> = emptyList()
override fun logException(
e: Exception,
map: Map<String, String>,
s: String
): List<ReportingMessage> = emptyList()
override fun logScreen(s: String, map: Map<String, String>): List<ReportingMessage> =
emptyList()
//endregion
//endregion
//region Push Listener Implementation
override fun onPushRegistration(deviceToken: String, senderId: String): Boolean {
// Saving the registration token to determine when the user uninstalls the app.
this.deviceToken = deviceToken
executeIfSingularInitialized({
if (MPUtility.isFirebaseAvailable()) {
Singular.setFCMDeviceToken(deviceToken)
}
}, forceInitSingular = false, "onPushRegistration")
return true
}
private fun executeIfSingularInitialized(
operation: () -> Unit,
forceInitSingular: Boolean = false,
operationName: String
) {
if (isInitialized) {
operation.invoke()
Logger.debug("$operationName executed")
} else {
if (forceInitSingular) {
initializeSingular()
executeIfSingularInitialized(operation, false, operationName)
} else {
Logger.debug("$operationName can't be executed, Singular not initialized")
}
}
}
private fun initializeSingular() {
if (!isInitialized) {
if (Singular.init(context, buildSingularConfig(settings))) {
currentUser?.id?.toString()?.let { Singular.setCustomUserId(it) }
isInitialized = true
singularSettings = settings
deviceToken?.let { deviceToken ->
if (MPUtility.isFirebaseAvailable()) {
Singular.setFCMDeviceToken(deviceToken)
}
}
}
}
}
//region Unimplemented (Empty Methods)
override fun willHandlePushMessage(intent: Intent): Boolean = false
override fun onPushMessageReceived(context: Context, intent: Intent) {}
//endregion
//endregion
//region Commerce Listener Implementation
override fun logEvent(commerceEvent: CommerceEvent): List<ReportingMessage> {
var list = emptyList<ReportingMessage>()
executeIfSingularInitialized(operation = {
if (commerceEvent.productAction == Product.PURCHASE) {
list = handlePurchaseEvents(commerceEvent)
} else {
list = handleNonPurchaseEvents(commerceEvent)
}
}, forceInitSingular = true, "logEvent")
return list
}
private fun handlePurchaseEvents(commerceEvent: CommerceEvent): List<ReportingMessage> {
val messages: MutableList<ReportingMessage> = ArrayList()
commerceEvent.products?.let {
for (product in it) {
Singular.revenue(
commerceEvent.currency,
product.totalAmount,
product.sku,
product.name,
product.category,
product.quantity.toInt(),
product.unitPrice
)
}
}
messages.add(ReportingMessage.fromEvent(this, commerceEvent))
return messages
}
private fun handleNonPurchaseEvents(commerceEvent: CommerceEvent): List<ReportingMessage> {
val messages: MutableList<ReportingMessage> = ArrayList()
// Getting the mParticle events from the commerce event
val eventList = CommerceEventUtils.expand(commerceEvent)
if (eventList != null) {
for (event in eventList) {
try {
logEvent(event)?.let {
for (message in it) {
messages.add(message)
}
}
} catch (e: Exception) {
Logger.warning("Failed to call logCustomEvent to Singular kit: $e")
}
}
}
return messages
}
//region Unimplemented (Empty Methods)
override fun logLtvIncrease(
bigDecimal: BigDecimal,
bigDecimal1: BigDecimal,
s: String,
map: Map<String, String>
): List<ReportingMessage> = emptyList()
//endregion
//endregion
//region Deprecated Attribute Listener
override fun setUserAttribute(key: String, value: String) {
// TODO: Debug these lines to understand the code
val map = HashMap<String?, String?>()
if (MParticle.UserAttributes.AGE == key) {
map[USER_AGE_KEY] = value
} else if (MParticle.UserAttributes.GENDER == key) {
if (value.contains("fe")) {
map[USER_GENDER_KEY] = "f"
} else {
map[USER_GENDER_KEY] = "m"
}
}
if (map.isNotEmpty()) {
executeIfSingularInitialized(
{
Singular.eventJSON("UserAttribute", (map as Map<*, *>?)?.let { JSONObject(it) })
}, forceInitSingular = false, "setUserAttribute"
)
}
}
override fun setUserAttributeList(s: String, list: List<String>) {}
override fun onIncrementUserAttribute(
key: String?,
incrementedBy: Number?,
value: String?,
user: FilteredMParticleUser?
) {
}
override fun onRemoveUserAttribute(s: String, filteredMParticleUser: FilteredMParticleUser) {}
override fun onSetUserAttribute(
s: String,
o: Any,
filteredMParticleUser: FilteredMParticleUser
) {
}
override fun onSetUserTag(s: String, filteredMParticleUser: FilteredMParticleUser) {}
override fun onSetUserAttributeList(
s: String,
list: List<String>,
filteredMParticleUser: FilteredMParticleUser
) {
}
override fun onSetAllUserAttributes(
map: Map<String, String>,
map1: Map<String, List<String>>,
filteredMParticleUser: FilteredMParticleUser
) {
}
override fun supportsAttributeLists(): Boolean {
return false
}
override fun onConsentStateUpdated(
consentState: ConsentState,
consentState1: ConsentState,
filteredMParticleUser: FilteredMParticleUser
) {
executeIfSingularInitialized({
consentState.ccpaConsentState?.let { Singular.limitDataSharing(it.isConsented) }
}, forceInitSingular = false, "onConsentStateUpdated")
}
override fun setAllUserAttributes(map: Map<String, String>, map1: Map<String, List<String>>) {}
override fun removeUserAttribute(s: String) {}
override fun setUserIdentity(identityType: IdentityType, s: String) {
if (identityType == IdentityType.CustomerId) {
executeIfSingularInitialized({
Singular.setCustomUserId(s)
}, forceInitSingular = false, "setUserIdentity")
}
}
override fun removeUserIdentity(identityType: IdentityType) {
if (identityType == IdentityType.CustomerId) {
executeIfSingularInitialized({
Singular.unsetCustomUserId()
isInitialized = false
}, forceInitSingular = false, "removeUserIdentity")
}
}
override fun logout(): List<ReportingMessage> {
val messageList: MutableList<ReportingMessage> = ArrayList()
executeIfSingularInitialized({
Singular.unsetCustomUserId()
isInitialized = false
messageList.add(ReportingMessage.logoutMessage(this))
}, forceInitSingular = false, "logout")
return messageList
}
override fun onApplicationForeground() {
// Handling deeplinks when the application resumes from background
initializeSingular()
}
override fun onApplicationBackground() {} //endregion
companion object {
//region Members
// Config Consts
private const val API_KEY = "apiKey"
private const val API_SECRET = "secret"
private const val DDL_TIMEOUT = "ddlTimeout"
private const val KIT_NAME = "Singular"
// User Attribute Consts
private const val USER_AGE_KEY = "age"
private const val USER_GENDER_KEY = "gender"
// Singular Link Consts
private const val PASSTHROUGH = "passthrough"
private const val IS_DEFERRED = "is_deferred"
// Wrapper Consts
private const val MPARTICLE_WRAPPER_NAME = "mParticle"
private const val MPARTICLE_WRAPPER_VERSION = "1.0.1"
private const val CANT_BUILD_SINGULAR_CONFIG_MESSAGE =
"Can't build Singular Config in the mParticle Kit"
private var singularSettings: Map<String, String>? = null
}
}