From 262ecd158c19457bde52b392a250af899026ebf6 Mon Sep 17 00:00:00 2001 From: markvdouw Date: Mon, 12 Jun 2023 16:32:15 -0300 Subject: [PATCH 1/3] feat: lazy initialization of the SDK based on events and Android lifecycle functions triggered (#104) * Lazy initialization of the SDK based on events and Android lifecycle functions triggered. * Changes due to PR comments --- .../kotlin/com/mparticle/kits/SingularKit.kt | 143 +++++++++++++----- 1 file changed, 103 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/SingularKit.kt b/src/main/kotlin/com/mparticle/kits/SingularKit.kt index a12affd..aaf5c54 100644 --- a/src/main/kotlin/com/mparticle/kits/SingularKit.kt +++ b/src/main/kotlin/com/mparticle/kits/SingularKit.kt @@ -14,7 +14,13 @@ 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.* +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 @@ -26,6 +32,8 @@ 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 @@ -36,16 +44,13 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, // 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 = ArrayList() - if (Singular.init(context, buildSingularConfig(settings))) { - singularSettings = settings - messages.add( - ReportingMessage( - this, - ReportingMessage.MessageType.APP_STATE_TRANSITION, - System.currentTimeMillis(), null - ) + messages.add( + ReportingMessage( + this, + ReportingMessage.MessageType.APP_STATE_TRANSITION, + System.currentTimeMillis(), null ) - } + ) return messages } @@ -120,8 +125,10 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, } //region Unimplemented (Empty Methods) - override fun onActivityCreated(activity: Activity, bundle: Bundle?): List = - emptyList() + override fun onActivityCreated(activity: Activity, bundle: Bundle?): List { + initializeSingular() + return emptyList() + } override fun onActivityStarted(activity: Activity): List = emptyList() @@ -140,21 +147,23 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, //region Event Listener Implementation override fun logEvent(mpEvent: MPEvent): List? { val messages: MutableList = ArrayList() - val eventName = mpEvent.eventName - val eventInfo = mpEvent.customAttributes + 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) - } + // 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)) - } + // 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 } @@ -178,12 +187,48 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, //region Push Listener Implementation override fun onPushRegistration(deviceToken: String, senderId: String): Boolean { // Saving the registration token to determine when the user uninstalls the app. - if (MPUtility.isFirebaseAvailable()) { - Singular.setFCMDeviceToken(deviceToken) - } + 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 @@ -193,11 +238,15 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, //endregion //region Commerce Listener Implementation override fun logEvent(commerceEvent: CommerceEvent): List { - return if (commerceEvent.productAction == Product.PURCHASE) { - handlePurchaseEvents(commerceEvent) - } else { - handleNonPurchaseEvents(commerceEvent) - } + var list = emptyList() + 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 { @@ -264,7 +313,11 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, } } if (map.isNotEmpty()) { - Singular.eventJSON("UserAttribute", (map as Map<*, *>?)?.let { JSONObject(it) }) + executeIfSingularInitialized( + { + Singular.eventJSON("UserAttribute", (map as Map<*, *>?)?.let { JSONObject(it) }) + }, forceInitSingular = false, "setUserAttribute" + ) } } @@ -312,7 +365,9 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, filteredMParticleUser: FilteredMParticleUser ) { - consentState.ccpaConsentState?.let { Singular.limitDataSharing(it.isConsented) } + executeIfSingularInitialized({ + consentState.ccpaConsentState?.let { Singular.limitDataSharing(it.isConsented) } + }, forceInitSingular = false, "onConsentStateUpdated") } @@ -320,26 +375,34 @@ open class SingularKit : KitIntegration(), ActivityListener, EventListener, override fun removeUserAttribute(s: String) {} override fun setUserIdentity(identityType: IdentityType, s: String) { if (identityType == IdentityType.CustomerId) { - Singular.setCustomUserId(s) + executeIfSingularInitialized({ + Singular.setCustomUserId(s) + }, forceInitSingular = false, "setUserIdentity") } } override fun removeUserIdentity(identityType: IdentityType) { if (identityType == IdentityType.CustomerId) { - Singular.unsetCustomUserId() + executeIfSingularInitialized({ + Singular.unsetCustomUserId() + isInitialized = false + }, forceInitSingular = false, "removeUserIdentity") } } override fun logout(): List { - Singular.unsetCustomUserId() val messageList: MutableList = ArrayList() - messageList.add(ReportingMessage.logoutMessage(this)) + 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 - Singular.init(context, buildSingularConfig(singularSettings)) + initializeSingular() } override fun onApplicationBackground() {} //endregion From 4a72f051d7a9ef77ed8dcdba5a4c07055b9c35de Mon Sep 17 00:00:00 2001 From: markvdouw Date: Thu, 20 Jul 2023 18:53:47 -0300 Subject: [PATCH 2/3] feat: SQDSDKS-5551 Changing test for sideloading kits feature (#115) Fixing test to accomodate new architecture change for sideloaded kits --- src/test/kotlin/com/mparticle/kits/SingularKitTest.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/com/mparticle/kits/SingularKitTest.kt b/src/test/kotlin/com/mparticle/kits/SingularKitTest.kt index 7d14364..4009618 100644 --- a/src/test/kotlin/com/mparticle/kits/SingularKitTest.kt +++ b/src/test/kotlin/com/mparticle/kits/SingularKitTest.kt @@ -1,6 +1,7 @@ package com.mparticle.kits import com.mparticle.MPEvent +import com.mparticle.MParticleOptions import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Product import com.singular.sdk.Singular @@ -12,6 +13,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException import org.mockito.Mock +import org.mockito.Mockito import org.powermock.core.classloader.annotations.PowerMockIgnore import org.powermock.modules.junit4.rule.PowerMockRule @@ -180,11 +182,12 @@ class KitTests { @Throws(Exception::class) @Test fun isSingularIntegrationInFactory() { - val factory = KitIntegrationFactory() - val integrations = factory.knownIntegrations - val className = SingularKit().javaClass.name + val options = Mockito.mock(MParticleOptions::class.java) + val factory = KitIntegrationFactory(options) + val integrations = factory.supportedKits.values + val className = kit?.javaClass?.name.orEmpty() for (integration in integrations) { - if (integration.value == className) { + if (integration.name.replace("SingularKit", "MockSingularKit") == className) { return } } From 270974914716c6f009169b5ddefac2d4731c0dd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:15:46 +0000 Subject: [PATCH 3/3] chore: bump com.singular.sdk:singular_sdk from 12.0.10 to 12.1.4 Bumps com.singular.sdk:singular_sdk from 12.0.10 to 12.1.4. --- updated-dependencies: - dependency-name: com.singular.sdk:singular_sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 931a6a0..075deb2 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ repositories { } dependencies { - api 'com.singular.sdk:singular_sdk:12.0.10' + api 'com.singular.sdk:singular_sdk:12.1.4' androidTestImplementation 'androidx.test:runner:1.5.2' testImplementation 'org.powermock:powermock-core:2.0.9'