diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ActivityBroadcasterPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ActivityBroadcasterPlugin.kt index 0976a1c4..d574ed70 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ActivityBroadcasterPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ActivityBroadcasterPlugin.kt @@ -1,17 +1,3 @@ -/* - * Creator: Debanjan Chatterjee on 20/11/23, 3:48 pm Last modified: 20/11/23, 3:48 pm - * Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain a - * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - package com.rudderstack.android.internal.infrastructure import android.app.Activity @@ -24,13 +10,19 @@ import com.rudderstack.core.InfrastructurePlugin import java.util.concurrent.atomic.AtomicInteger /** Tracks the Activities in the application and broadcasts the same */ -internal class ActivityBroadcasterPlugin( -) : InfrastructurePlugin { +internal class ActivityBroadcasterPlugin : InfrastructurePlugin { + override lateinit var analytics: Analytics private val application: Application? - get() = analytics?.currentConfigurationAndroid?.application - private var analytics: Analytics? = null + get() = analytics.currentConfigurationAndroid?.application + private val activityCount = AtomicInteger() + + override fun setup(analytics: Analytics) { + super.setup(analytics) + application?.registerActivityLifecycleCallbacks(lifecycleCallback) + } + private val lifecycleCallback by lazy { object : Application.ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { @@ -53,7 +45,7 @@ internal class ActivityBroadcasterPlugin( } override fun onActivityStopped(activity: Activity) { - if (analytics?.currentConfigurationAndroid?.trackLifecycleEvents == true) { + if (analytics.currentConfigurationAndroid?.trackLifecycleEvents == true) { decrementActivityCount() if (activityCount.get() == 0) { broadCastApplicationStop() @@ -72,13 +64,13 @@ internal class ActivityBroadcasterPlugin( } private fun broadcastActivityStart(activity: Activity) { - analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is LifecycleListenerPlugin) { setCurrentActivity(activity) onActivityStarted(activity.localClassName) } } - analytics?.applyMessageClosure { + analytics.applyMessageClosure { if (this is LifecycleListenerPlugin) { setCurrentActivity(activity) onActivityStarted(activity.localClassName) @@ -96,12 +88,12 @@ internal class ActivityBroadcasterPlugin( private fun broadCastApplicationStart() { - analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is LifecycleListenerPlugin) { this.onAppForegrounded() } } - analytics?.applyMessageClosure { + analytics.applyMessageClosure { if (this is LifecycleListenerPlugin) { this.onAppForegrounded() } @@ -109,13 +101,13 @@ internal class ActivityBroadcasterPlugin( } private fun broadCastApplicationStop() { - analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is LifecycleListenerPlugin) { setCurrentActivity(null) this.onAppBackgrounded() } } - analytics?.applyMessageClosure { + analytics.applyMessageClosure { if (this is LifecycleListenerPlugin) { setCurrentActivity(null) @@ -124,11 +116,6 @@ internal class ActivityBroadcasterPlugin( } } - override fun setup(analytics: Analytics) { - this.analytics = analytics - application?.registerActivityLifecycleCallbacks(lifecycleCallback) - } - override fun shutdown() { application?.unregisterActivityLifecycleCallbacks(lifecycleCallback) } diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/AnonymousIdHeaderPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/AnonymousIdHeaderPlugin.kt index cb9e8084..5ff69ca2 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/AnonymousIdHeaderPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/AnonymousIdHeaderPlugin.kt @@ -1,17 +1,3 @@ -/* - * Creator: Debanjan Chatterjee on 29/11/23, 4:58 pm Last modified: 29/11/23, 4:58 pm - * Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain a - * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - package com.rudderstack.android.internal.infrastructure import com.rudderstack.android.AndroidUtils @@ -22,26 +8,30 @@ import com.rudderstack.core.Configuration import com.rudderstack.core.DataUploadService import com.rudderstack.core.InfrastructurePlugin -internal class AnonymousIdHeaderPlugin : InfrastructurePlugin{ +internal class AnonymousIdHeaderPlugin : InfrastructurePlugin { + + override lateinit var analytics: Analytics private var dataUploadService: DataUploadService? = null - private var _analytics: Analytics? = null + override fun setup(analytics: Analytics) { - _analytics = analytics + super.setup(analytics) dataUploadService = analytics.dataUploadService } - override fun shutdown() { - dataUploadService = null - _analytics = null - } - override fun updateConfiguration(configuration: Configuration) { - if(configuration !is ConfigurationAndroid) return - val anonId = configuration.anonymousId?: AndroidUtils.generateAnonymousId(configuration.collectDeviceId, configuration.application).also { - _analytics?.applyConfigurationAndroid { - copy(anonymousId = it) - } + if (configuration !is ConfigurationAndroid) return + val anonId = configuration.anonymousId ?: AndroidUtils.generateAnonymousId( + configuration.collectDeviceId, + configuration.application + ).also { + analytics.applyConfigurationAndroid { + copy(anonymousId = it) + } } - dataUploadService?.addHeaders(mapOf("Anonymous-Id" to anonId)) + dataUploadService?.addHeaders(mapOf("Anonymous-Id" to anonId)) + } + + override fun shutdown() { + dataUploadService = null } -} \ No newline at end of file +} diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/AppInstallUpdateTrackerPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/AppInstallUpdateTrackerPlugin.kt index 615fed41..0fa6c094 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/AppInstallUpdateTrackerPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/AppInstallUpdateTrackerPlugin.kt @@ -2,13 +2,13 @@ package com.rudderstack.android.internal.infrastructure import android.content.pm.PackageManager import android.os.Build +import com.rudderstack.android.storage.AndroidStorage import com.rudderstack.android.utilities.androidStorage import com.rudderstack.android.utilities.currentConfigurationAndroid -import com.rudderstack.android.storage.AndroidStorage -import com.rudderstack.core.models.AppVersion import com.rudderstack.core.Analytics import com.rudderstack.core.InfrastructurePlugin import com.rudderstack.core.Plugin +import com.rudderstack.core.models.AppVersion import com.rudderstack.core.models.Message private const val PREVIOUS_VERSION = "previous_version" @@ -31,7 +31,7 @@ private const val DEFAULT_VERSION_NAME = "" * * */ class AppInstallUpdateTrackerPlugin : Plugin { - private var analytics: Analytics? = null + override lateinit var analytics: Analytics private lateinit var appVersion: AppVersion override fun intercept(chain: Plugin.Chain): Message { // no change made to message @@ -39,10 +39,10 @@ class AppInstallUpdateTrackerPlugin : Plugin { } override fun setup(analytics: Analytics) { - this.analytics = analytics + super.setup(analytics) this.appVersion = getAppVersion(analytics) storeVersionNameAndBuild(analytics.androidStorage) - if (this.analytics?.currentConfigurationAndroid?.trackLifecycleEvents == true) { + if (this.analytics.currentConfigurationAndroid?.trackLifecycleEvents == true) { trackApplicationStatus() } } @@ -100,7 +100,7 @@ class AppInstallUpdateTrackerPlugin : Plugin { } private fun sendApplicationInstalledEvent() { - this.analytics?.logger?.debug(log = "Tracking Application Installed event") + this.analytics.logger.debug(log = "Tracking Application Installed event") val trackProperties = mutableMapOf() trackProperties[VERSION] = this.appVersion.currentVersionName trackProperties[BUILD] = this.appVersion.currentBuild @@ -109,26 +109,21 @@ class AppInstallUpdateTrackerPlugin : Plugin { } private fun sendApplicationUpdatedEvent() { - this.analytics?.logger?.debug(log = "Tracking Application Updated event") + this.analytics.logger.debug(log = "Tracking Application Updated event") val trackProperties = mutableMapOf() trackProperties[PREVIOUS_VERSION] = this.appVersion.previousVersionName trackProperties[PREVIOUS_BUILD] = this.appVersion.previousBuild trackProperties[VERSION] = this.appVersion.currentVersionName trackProperties[BUILD] = this.appVersion.currentBuild - sendEvent(EVENT_NAME_APPLICATION_UPDATED, trackProperties) } private fun sendEvent(eventName: String, properties: Map) { - analytics?.track { + analytics.track { event(eventName) trackProperties { add(properties) } } } - - override fun onShutDown() { - analytics = null - } } diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/LifecycleObserverPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/LifecycleObserverPlugin.kt index bd8a6ca9..7e75e6d2 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/LifecycleObserverPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/LifecycleObserverPlugin.kt @@ -25,13 +25,15 @@ private const val AUTOMATIC = "automatic" * * @property currentMillisGenerator */ -internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) = { SystemClock.uptimeMillis() }) : - Plugin, LifecycleListenerPlugin { +internal class LifecycleObserverPlugin( + val currentMillisGenerator: (() -> Long) = { SystemClock.uptimeMillis() } +) : Plugin, LifecycleListenerPlugin { + + override lateinit var analytics: Analytics private var _isFirstLaunch = AtomicBoolean(true) private var _lastSuccessfulDownloadTimeInMillis = AtomicLong(-1L) - private var analytics: Analytics? = null private var currentActivityName: String? = null private val listener = ConfigDownloadService.Listener { if (it) { @@ -39,9 +41,18 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) } } + override fun setup(analytics: Analytics) { + super.setup(analytics) + analytics.applyInfrastructureClosure { + if (this is ConfigDownloadService) { + addListener(listener, 1) + } + } + } + private fun sendLifecycleStart() { withTrackLifeCycle { - analytics?.also { analytics -> + analytics.also { analytics -> analytics.track { event(EVENT_NAME_APPLICATION_OPENED) trackProperties { @@ -58,7 +69,7 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) private fun sendLifecycleStop() { withTrackLifeCycle { - analytics?.track { + analytics.track { event(EVENT_NAME_APPLICATION_STOPPED) } } @@ -68,23 +79,13 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) return chain.proceed(chain.message()) } - override fun setup(analytics: Analytics) { - this.analytics = analytics - analytics.applyInfrastructureClosure { - if (this is ConfigDownloadService) { - addListener(listener, 1) - } - } - } - override fun onShutDown() { _lastSuccessfulDownloadTimeInMillis.set(0) - analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is ConfigDownloadService) { removeListener(listener) } } - analytics = null } override fun updateConfiguration(configuration: Configuration) { @@ -97,22 +98,22 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) } private fun checkAndDownloadSourceConfig() { - analytics?.takeIf { it.currentConfiguration?.shouldVerifySdk == true }?.apply { + analytics.takeIf { it.currentConfiguration?.shouldVerifySdk == true }?.apply { if (currentMillisGenerator() - (_lastSuccessfulDownloadTimeInMillis.get()) >= MAX_CONFIG_DOWNLOAD_INTERVAL) { - analytics?.updateSourceConfig() + analytics.updateSourceConfig() } } } override fun onAppBackgrounded() { sendLifecycleStop() - analytics?.flush() + analytics.flush() } override fun onActivityStarted(activityName: String) { currentActivityName = activityName withRecordScreenViews { - analytics?.screen { + analytics.screen { screenName(activityName) screenProperties { add(AUTOMATIC to true) @@ -128,7 +129,7 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) override fun onScreenChange(name: String, arguments: Map?) { val activityName = currentActivityName ?: "" withRecordScreenViews { - analytics?.screen { + analytics.screen { screenName(activityName) this.category(name) this.screenProperties { @@ -139,13 +140,13 @@ internal class LifecycleObserverPlugin(val currentMillisGenerator: (() -> Long) } private fun withTrackLifeCycle(body: () -> Unit) { - if (analytics?.currentConfigurationAndroid?.trackLifecycleEvents == true) { + if (analytics.currentConfigurationAndroid?.trackLifecycleEvents == true) { body() } } private fun withRecordScreenViews(body: () -> Unit) { - if (analytics?.currentConfigurationAndroid?.recordScreenViews == true) { + if (analytics.currentConfigurationAndroid?.recordScreenViews == true) { body() } } diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ReinstatePlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ReinstatePlugin.kt index 9262aaaa..8dbd23d5 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ReinstatePlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ReinstatePlugin.kt @@ -22,16 +22,24 @@ import com.rudderstack.core.models.createContext * traits and external ids */ internal class ReinstatePlugin : InfrastructurePlugin { - private var _analytics: Analytics? = null + + override lateinit var analytics: Analytics + + override fun setup(analytics: Analytics) { + super.setup(analytics) + setReinstated(false) + reinstate() + } + private fun setReinstated(isReinstated: Boolean) { synchronized(this) { if (isReinstated) { - _analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is DataUploadService) this.resume() } } else { - _analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is DataUploadService) this.pause() } @@ -39,12 +47,6 @@ internal class ReinstatePlugin : InfrastructurePlugin { } } - override fun setup(analytics: Analytics) { - _analytics = analytics - setReinstated(false) - reinstate() - } - private fun reinstate() { if (isV2DataAvailable()) { reinstateV2FromCache() @@ -52,61 +54,60 @@ internal class ReinstatePlugin : InfrastructurePlugin { return } migrateV1DataIfAvailable() - if (_analytics?.currentConfigurationAndroid?.anonymousId == null) { - _analytics?.currentConfigurationAndroid?.fillDefaults() + if (analytics.currentConfigurationAndroid?.anonymousId == null) { + analytics.currentConfigurationAndroid?.fillDefaults() setReinstated(true) return } } private fun ConfigurationAndroid.fillDefaults() { - _analytics?.setAnonymousId(AndroidUtils.generateAnonymousId(collectDeviceId, application)) - _analytics?.initializeSessionManagement( - _analytics?.androidStorage?.sessionId, - _analytics?.androidStorage?.lastActiveTimestamp + analytics?.setAnonymousId(AndroidUtils.generateAnonymousId(collectDeviceId, application)) + analytics?.initializeSessionManagement( + analytics?.androidStorage?.sessionId, + analytics?.androidStorage?.lastActiveTimestamp ) } private fun reinstateV2FromCache() { - val anonId = _analytics?.androidStorage?.anonymousId - ?: _analytics?.currentConfigurationAndroid?.let { - AndroidUtils.generateAnonymousId( - it.collectDeviceId, - it.application - ) - } - val context = _analytics?.androidStorage?.context + val anonId = analytics.androidStorage.anonymousId ?: analytics.currentConfigurationAndroid?.let { + AndroidUtils.generateAnonymousId( + it.collectDeviceId, + it.application + ) + } + val context = analytics.androidStorage.context context?.let { - _analytics?.processNewContext(context) + analytics.processNewContext(context) } if (anonId != null) - _analytics?.setAnonymousId(anonId) - _analytics?.initializeSessionManagement( - _analytics?.androidStorage?.sessionId, _analytics?.androidStorage?.lastActiveTimestamp + analytics.setAnonymousId(anonId) + analytics.initializeSessionManagement( + analytics.androidStorage.sessionId, analytics.androidStorage.lastActiveTimestamp ) } private fun isV2DataAvailable(): Boolean { - return !_analytics?.androidStorage?.anonymousId.isNullOrEmpty() || - !_analytics?.androidStorage?.userId.isNullOrEmpty() || - !_analytics?.contextState?.value.isNullOrEmpty() + return !analytics.androidStorage.anonymousId.isNullOrEmpty() || + !analytics.androidStorage.userId.isNullOrEmpty() || + !analytics.contextState?.value.isNullOrEmpty() } private fun migrateV1DataIfAvailable() { // migrate user id/ anon id - _analytics?.setUserIdFromV1() - _analytics?.migrateAnonymousIdFromV1() - _analytics?.migrateOptOutFromV1() - _analytics?.migrateContextFromV1() + analytics.setUserIdFromV1() + analytics.migrateAnonymousIdFromV1() + analytics.migrateOptOutFromV1() + analytics.migrateContextFromV1() //we do not store v1 advertising id - _analytics?.androidStorage?.resetV1AdvertisingId() - _analytics?.migrateSession() - _analytics?.migrateV1LifecycleProperties() - _analytics?.androidStorage?.migrateV1StorageToV2 { + analytics.androidStorage.resetV1AdvertisingId() + analytics.migrateSession() + analytics.migrateV1LifecycleProperties() + analytics.androidStorage.migrateV1StorageToV2 { setReinstated(true) } - _analytics?.androidStorage?.deleteV1SharedPreferencesFile() - _analytics?.androidStorage?.deleteV1ConfigFiles() + analytics.androidStorage.deleteV1SharedPreferencesFile() + analytics.androidStorage.deleteV1ConfigFiles() } private fun Analytics.migrateSession() { @@ -137,7 +138,7 @@ internal class ReinstatePlugin : InfrastructurePlugin { collectDeviceId, application )).let { logger.error(log = "Unable to migrate anonymousId from V1. Generating new anonymousId") - _analytics?.setAnonymousId(it) + analytics.setAnonymousId(it) } androidStorage.resetV1AnonymousId() } @@ -171,7 +172,7 @@ internal class ReinstatePlugin : InfrastructurePlugin { private fun Analytics.migrateOptOutFromV1() { val optOut = androidStorage.v1OptOut if (!optOut || this.androidStorage.isOptedOut) return //only relevant if optout is true - _analytics?.optOut(true) + analytics.optOut(true) androidStorage.resetV1OptOut() } @@ -184,9 +185,4 @@ internal class ReinstatePlugin : InfrastructurePlugin { androidStorage.resetV1ExternalIds() } } - - override fun shutdown() { - _analytics = null - } - } diff --git a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ResetImplementationPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ResetImplementationPlugin.kt index 30b2c9ea..dc058bbe 100644 --- a/android/src/main/java/com/rudderstack/android/internal/infrastructure/ResetImplementationPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/infrastructure/ResetImplementationPlugin.kt @@ -8,21 +8,18 @@ import com.rudderstack.core.models.createContext import com.rudderstack.core.models.updateWith class ResetImplementationPlugin : InfrastructurePlugin { - private var _analytics: Analytics? = null - override fun setup(analytics: Analytics) { - _analytics = analytics - } - private val contextState - get() =_analytics?.contextState - override fun reset() { - _analytics?.processNewContext(contextState?.value?.updateWith(traits = mapOf(), - externalIds = listOf() - ) ?: createContext()) - } + override lateinit var analytics: Analytics - override fun shutdown() { - //nothing to implement - } + private val contextState + get() = analytics.contextState + override fun reset() { + analytics.processNewContext( + contextState?.value?.updateWith( + traits = mapOf(), + externalIds = listOf() + ) ?: createContext() + ) + } } diff --git a/android/src/main/java/com/rudderstack/android/internal/plugins/ExtractStatePlugin.kt b/android/src/main/java/com/rudderstack/android/internal/plugins/ExtractStatePlugin.kt index 554dd41d..d84a2bc9 100644 --- a/android/src/main/java/com/rudderstack/android/internal/plugins/ExtractStatePlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/plugins/ExtractStatePlugin.kt @@ -7,7 +7,6 @@ import com.rudderstack.android.utilities.empty import com.rudderstack.android.utilities.processNewContext import com.rudderstack.core.Analytics import com.rudderstack.core.Plugin -import com.rudderstack.core.optAdd import com.rudderstack.core.models.AliasMessage import com.rudderstack.core.models.IdentifyMessage import com.rudderstack.core.models.Message @@ -15,6 +14,7 @@ import com.rudderstack.core.models.MessageContext import com.rudderstack.core.models.optAddContext import com.rudderstack.core.models.traits import com.rudderstack.core.models.updateWith +import com.rudderstack.core.optAdd /** * Mutates the system state, if required, based on the Event. @@ -22,11 +22,7 @@ import com.rudderstack.core.models.updateWith */ internal class ExtractStatePlugin : Plugin { - private var _analytics: Analytics? = null - override fun setup(analytics: Analytics) { - super.setup(analytics) - _analytics = analytics - } + override lateinit var analytics: Analytics override fun intercept(chain: Plugin.Chain): Message { val message = chain.message() @@ -38,11 +34,11 @@ internal class ExtractStatePlugin : Plugin { //save and update traits //update userId //save and update external ids - (message.context ?: return message).let{ - if(it.traits?.get("anonymousId") == null) - _analytics?.currentConfigurationAndroid?.anonymousId.let { anonId -> + (message.context ?: return message).let { + if (it.traits?.get("anonymousId") == null) + analytics.currentConfigurationAndroid?.anonymousId.let { anonId -> it.updateWith(traits = it.traits optAdd ("anonymousId" to anonId)) - }else it + } else it }.let { //alias and identify messages are expected to contain user id. @@ -51,22 +47,25 @@ internal class ExtractStatePlugin : Plugin { //aforementioned ids val newUserId = getUserId(message) - _analytics?.logger?.debug(log = "New user id detected: $newUserId") - val prevId = _analytics?.androidStorage?.userId ?: _analytics?.currentConfigurationAndroid?.anonymousId ?: String.empty() + analytics.logger.debug(log = "New user id detected: $newUserId") + val prevId = analytics.androidStorage.userId ?: analytics.currentConfigurationAndroid?.anonymousId ?: String.empty() // in case of identify, the stored traits (if any) are replaced by the ones provided // if user id is different. else traits are added to it val msg = when (message) { is AliasMessage -> { // in case of alias, we change the user id in traits - newUserId?.let { newId-> updateNewAndPrevUserIdInContext( - newId, it - )}?.let { + newUserId?.let { newId -> + updateNewAndPrevUserIdInContext( + newId, it + ) + }?.let { replaceContext(it) message.copy(context = it, userId = newUserId, previousId = prevId) } } + is IdentifyMessage -> { - val updatedContext = if(newUserId != prevId) { + val updatedContext = if (newUserId != prevId) { it } else { appendContextForIdentify(it) @@ -74,13 +73,14 @@ internal class ExtractStatePlugin : Plugin { replaceContext(updatedContext) message.copy(context = updatedContext) } + else -> { message } - }?:message + } ?: message msg.also { newUserId?.let { id -> - _analytics?.androidStorage?.setUserId(id) + analytics.androidStorage.setUserId(id) } } return chain.proceed(msg) @@ -88,18 +88,17 @@ internal class ExtractStatePlugin : Plugin { } } - private fun appendContextForIdentify(messageContext: MessageContext) : MessageContext{ - return _analytics?.contextState?.value?.let { savedContext -> - messageContext optAddContext savedContext - }?: messageContext + private fun appendContextForIdentify(messageContext: MessageContext): MessageContext { + return analytics.contextState?.value?.let { savedContext -> + messageContext optAddContext savedContext + } ?: messageContext } private fun replaceContext(messageContext: MessageContext) { - _analytics?.processNewContext(messageContext) + analytics.processNewContext(messageContext) } - /** * Checks in the order * "user_id" key at root @@ -112,21 +111,21 @@ internal class ExtractStatePlugin : Plugin { * */ private fun getUserId(message: Message): String? { - return message.context?.let { + return message.context?.let { (it[KeyConstants.CONTEXT_USER_ID_KEY] - ?: (it.traits?.get(KeyConstants.CONTEXT_USER_ID_KEY)) - ?: (it[KeyConstants.CONTEXT_USER_ID_KEY_ALIAS]) - ?: (it.traits?.get(KeyConstants.CONTEXT_USER_ID_KEY_ALIAS)) - ?: (it[KeyConstants.CONTEXT_ID_KEY]) - ?: (it.traits?.get(KeyConstants.CONTEXT_ID_KEY)))?.toString() - }?:message.userId + ?: (it.traits?.get(KeyConstants.CONTEXT_USER_ID_KEY)) + ?: (it[KeyConstants.CONTEXT_USER_ID_KEY_ALIAS]) + ?: (it.traits?.get(KeyConstants.CONTEXT_USER_ID_KEY_ALIAS)) + ?: (it[KeyConstants.CONTEXT_ID_KEY]) + ?: (it.traits?.get(KeyConstants.CONTEXT_ID_KEY)))?.toString() + } ?: message.userId } private fun updateNewAndPrevUserIdInContext( newUserId: String, messageContext: MessageContext ): MessageContext { val newTraits = - messageContext.traits optAdd mapOf( + messageContext.traits optAdd mapOf( KeyConstants.CONTEXT_ID_KEY to newUserId, KeyConstants.CONTEXT_USER_ID_KEY to newUserId ) @@ -142,5 +141,4 @@ internal class ExtractStatePlugin : Plugin { const val CONTEXT_USER_ID_KEY_ALIAS = "userId" const val CONTEXT_ID_KEY = "id" } - } diff --git a/android/src/main/java/com/rudderstack/android/internal/plugins/FillDefaultsPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/plugins/FillDefaultsPlugin.kt index 08a36cc7..0b090395 100644 --- a/android/src/main/java/com/rudderstack/android/internal/plugins/FillDefaultsPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/plugins/FillDefaultsPlugin.kt @@ -4,10 +4,8 @@ import com.rudderstack.android.utilities.androidStorage import com.rudderstack.android.utilities.contextState import com.rudderstack.android.utilities.currentConfigurationAndroid import com.rudderstack.core.Analytics -import com.rudderstack.core.Plugin import com.rudderstack.core.MissingPropertiesException -import com.rudderstack.core.models.updateWith -import com.rudderstack.core.models.withExternalIdsRemoved +import com.rudderstack.core.Plugin import com.rudderstack.core.models.* /** @@ -26,11 +24,8 @@ import com.rudderstack.core.models.* */ internal class FillDefaultsPlugin : Plugin { - private var _analytics: Analytics? = null - override fun setup(analytics: Analytics) { - super.setup(analytics) - _analytics = analytics - } + override lateinit var analytics: Analytics + /** * Fill default details for [Message] * If message contains context, this will replace the ones present @@ -38,11 +33,11 @@ internal class FillDefaultsPlugin : Plugin { */ @Throws(MissingPropertiesException::class) private inline fun T.withDefaults(): T { - val anonId = this.anonymousId ?: _analytics?.currentConfigurationAndroid?.anonymousId - val userId = this.userId ?: _analytics?.androidStorage?.userId + val anonId = this.anonymousId ?: analytics.currentConfigurationAndroid?.anonymousId + val userId = this.userId ?: analytics.androidStorage.userId if (anonId == null && userId == null) { val ex = MissingPropertiesException("Either Anonymous Id or User Id must be present"); - _analytics?.currentConfigurationAndroid?.logger?.error( + analytics.currentConfigurationAndroid?.logger?.error( log = "Missing both anonymous Id and user Id. Use settings to update " + "anonymous id in Analytics constructor", throwable = ex ) @@ -50,22 +45,24 @@ internal class FillDefaultsPlugin : Plugin { } //copying top level context to message context val newContext = - // in case of alias we purposefully remove traits from context - _analytics?.contextState?.value?.let { - if (this is AliasMessage && this.userId != _analytics?.androidStorage?.userId) it.updateWith( - traits = mapOf() - ) else it - } selectiveReplace context.let { - if (this !is IdentifyMessage) { - // remove any external ids present in the message - // this is in accordance to v1 - it?.withExternalIdsRemoved() - } else it - } + // in case of alias we purposefully remove traits from context + analytics.contextState?.value?.let { + if (this is AliasMessage && this.userId != analytics.androidStorage.userId) it.updateWith( + traits = mapOf() + ) else it + } selectiveReplace context.let { + if (this !is IdentifyMessage) { + // remove any external ids present in the message + // this is in accordance to v1 + it?.withExternalIdsRemoved() + } else it + } - return (this.copy(context = newContext, + return (this.copy( + context = newContext, anonymousId = anonId, - userId = userId) as T) + userId = userId + ) as T) } private infix fun MessageContext?.selectiveReplace(context: MessageContext?): MessageContext? { diff --git a/android/src/main/java/com/rudderstack/android/internal/plugins/PlatformInputsPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/plugins/PlatformInputsPlugin.kt index 8f38b816..09ce86aa 100644 --- a/android/src/main/java/com/rudderstack/android/internal/plugins/PlatformInputsPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/plugins/PlatformInputsPlugin.kt @@ -51,15 +51,16 @@ import java.util.concurrent.atomic.AtomicReference private const val CHANNEL = "mobile" internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { - //if true collects advertising id automatically + + override lateinit var analytics: Analytics private val application - get() = _analytics?.currentConfigurationAndroid?.application + get() = analytics.currentConfigurationAndroid?.application private var autoCollectAdvertisingId = false set(value) { field = value if (value && _advertisingId.isNullOrEmpty()) application?.collectAdvertisingId() - else if(!value) synchronized(this) { + else if (!value) synchronized(this) { _advertisingId = null } } @@ -77,7 +78,6 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { private var _advertisingId: String? = null private var _deviceToken: String? = null - private var _analytics: Analytics? = null private val _currentActivity: AtomicReference = AtomicReference() private val currentActivity: Activity? get() = _currentActivity.get() @@ -93,7 +93,6 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { override fun setup(analytics: Analytics) { super.setup(analytics) - _analytics = analytics analytics.currentConfigurationAndroid?.updateAdvertisingValues() } @@ -104,7 +103,7 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { } private fun ConfigurationAndroid.updateAdvertisingValues() { - if(!advertisingId.isNullOrEmpty()) { + if (!advertisingId.isNullOrEmpty()) { synchronized(this) { if (_advertisingId != advertisingId) _advertisingId = advertisingId @@ -135,26 +134,29 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { } private fun Application.collectAdvertisingId() { - if(!isOnClassPath("com.google.android.gms.ads.identifier.AdvertisingIdClient")){ - _analytics?.currentConfiguration?.logger?.debug(log = "Not collecting advertising ID because " - + "com.google.android.gms.ads.identifier.AdvertisingIdClient " - + "was not found on the classpath." + if (!isOnClassPath("com.google.android.gms.ads.identifier.AdvertisingIdClient")) { + analytics.currentConfiguration?.logger?.debug( + log = "Not collecting advertising ID because " + + "com.google.android.gms.ads.identifier.AdvertisingIdClient " + + "was not found on the classpath." ) return } - _analytics?.currentConfigurationAndroid?.advertisingIdFetchExecutor?.submit { + analytics.currentConfigurationAndroid?.advertisingIdFetchExecutor?.submit { val adId = try { getGooglePlayServicesAdvertisingID() } catch (ex: Exception) { - _analytics?.currentConfiguration?.logger?.error(log = "Error collecting play services ad id", throwable = ex) + analytics.currentConfiguration?.logger?.error(log = "Error collecting play services ad id", throwable = ex) null - } ?: try { getAmazonFireAdvertisingID() } catch (ex: Exception){ - _analytics?.currentConfiguration?.logger?.error(log = "Error collecting amazon fire ad id", throwable = ex) + } ?: try { + getAmazonFireAdvertisingID() + } catch (ex: Exception) { + analytics.currentConfiguration?.logger?.error(log = "Error collecting amazon fire ad id", throwable = ex) null } - _analytics?.currentConfiguration?.logger?.info(log = "Ad id collected is $adId") + analytics.currentConfiguration?.logger?.info(log = "Ad id collected is $adId") if (adId != null) { - _analytics?.applyConfigurationAndroid { + analytics.applyConfigurationAndroid { copy(advertisingId = adId) } } @@ -167,19 +169,16 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { } } - @Throws(Exception::class) private fun Application.getGooglePlayServicesAdvertisingID(): String? { - - val advertisingInfo = - Class.forName("com.google.android.gms.ads.identifier.AdvertisingIdClient") - .getMethod("getAdvertisingIdInfo", Context::class.java).invoke(null, this) - ?: return null + val advertisingInfo = Class.forName("com.google.android.gms.ads.identifier.AdvertisingIdClient") + .getMethod("getAdvertisingIdInfo", Context::class.java).invoke(null, this) + ?: return null val isLimitAdTrackingEnabled = advertisingInfo.javaClass.getMethod("isLimitAdTrackingEnabled") .invoke(advertisingInfo) as? Boolean if (isLimitAdTrackingEnabled == true) { - _analytics?.logger?.debug(log = "Not collecting advertising ID because isLimitAdTrackingEnabled (Google Play Services) is true.") + analytics.logger.debug(log = "Not collecting advertising ID because isLimitAdTrackingEnabled (Google Play Services) is true.") return null } return advertisingInfo.javaClass.getMethod("getId").invoke(advertisingInfo) as? String @@ -191,7 +190,7 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { val contentResolver: ContentResolver = contentResolver val limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0 if (limitAdTracking) { - _analytics?.logger?.debug(log = "Not collecting advertising ID because limit_ad_tracking (Amazon Fire OS) is true.") + analytics.logger.debug(log = "Not collecting advertising ID because limit_ad_tracking (Amazon Fire OS) is true.") return null } return Settings.Secure.getString( @@ -233,7 +232,7 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { "version" to packageInfo.versionName ) } catch (ex: PackageManager.NameNotFoundException) { - _analytics?.currentConfiguration?.logger?.error( + analytics.currentConfiguration?.logger?.error( log = "Package Name Not Found", throwable = ex ) @@ -280,9 +279,9 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { // wifi enabled val isWifiEnabled = try { - (this.getSystemService(Context.WIFI_SERVICE) as? WifiManager)?.isWifiEnabled?:false + (this.getSystemService(Context.WIFI_SERVICE) as? WifiManager)?.isWifiEnabled ?: false } catch (ex: Exception) { - _analytics?.currentConfiguration?.logger?.error(log = "Cannot detect wifi. Wifi Permission not available") + analytics.currentConfiguration?.logger?.error(log = "Cannot detect wifi. Wifi Permission not available") false } @@ -328,8 +327,7 @@ internal class PlatformInputsPlugin : Plugin, LifecycleListenerPlugin { override fun onShutDown() { super.onShutDown() - _analytics?.currentConfigurationAndroid?.advertisingIdFetchExecutor?.shutdownNow() - _analytics = null + analytics.currentConfigurationAndroid?.advertisingIdFetchExecutor?.shutdownNow() } } diff --git a/android/src/main/java/com/rudderstack/android/internal/plugins/SessionPlugin.kt b/android/src/main/java/com/rudderstack/android/internal/plugins/SessionPlugin.kt index a569fdea..f84799e4 100644 --- a/android/src/main/java/com/rudderstack/android/internal/plugins/SessionPlugin.kt +++ b/android/src/main/java/com/rudderstack/android/internal/plugins/SessionPlugin.kt @@ -3,6 +3,7 @@ package com.rudderstack.android.internal.plugins import com.rudderstack.android.ConfigurationAndroid import com.rudderstack.android.internal.extensions.withSessionId import com.rudderstack.android.internal.extensions.withSessionStart +import com.rudderstack.android.models.UserSession import com.rudderstack.android.utilities.defaultLastActiveTimestamp import com.rudderstack.android.utilities.resetSession import com.rudderstack.android.utilities.startSessionIfNeeded @@ -12,35 +13,33 @@ import com.rudderstack.core.Analytics import com.rudderstack.core.Configuration import com.rudderstack.core.Plugin import com.rudderstack.core.models.Message -import com.rudderstack.android.models.UserSession internal class SessionPlugin : Plugin { - private var _analytics: Analytics? = null + override lateinit var analytics: Analytics + private var currentConfiguration: ConfigurationAndroid? = null - override fun setup(analytics: Analytics) { - super.setup(analytics) - _analytics = analytics - } override fun updateConfiguration(configuration: Configuration) { if (configuration !is ConfigurationAndroid) return if (currentConfiguration?.trackAutoSession == configuration.trackAutoSession - && currentConfiguration?.trackLifecycleEvents == configuration.trackLifecycleEvents) return - if( !configuration.trackAutoSession || !configuration.trackLifecycleEvents) { - _analytics?.updateSessionEnd() + && currentConfiguration?.trackLifecycleEvents == configuration.trackLifecycleEvents + ) return + if (!configuration.trackAutoSession || !configuration.trackLifecycleEvents) { + analytics.updateSessionEnd() return } - _analytics?.startSessionIfNeeded() + analytics.startSessionIfNeeded() } + override fun intercept(chain: Plugin.Chain): Message { //apply session id and session start // update last active timestamp // if difference between two events is more than session timeout, refresh session val message = chain.message() - _analytics?.startSessionIfNeeded() - val newMsg = _analytics?.userSessionState?.value?.takeIf { it.isActive }?.let { + analytics.startSessionIfNeeded() + val newMsg = analytics.userSessionState?.value?.takeIf { it.isActive }?.let { updateWithSession(message, it) } ?: message @@ -62,12 +61,12 @@ internal class SessionPlugin : Plugin { val updatedSession = it.copy( lastActiveTimestamp = defaultLastActiveTimestamp, sessionStart = false ) - _analytics?.userSessionState?.update(updatedSession) + analytics.userSessionState?.update(updatedSession) } override fun reset() { - _analytics?.resetSession() + analytics.resetSession() } } diff --git a/android/src/main/java/com/rudderstack/android/storage/AndroidStorageImpl.kt b/android/src/main/java/com/rudderstack/android/storage/AndroidStorageImpl.kt index 7ea21328..7454e123 100644 --- a/android/src/main/java/com/rudderstack/android/storage/AndroidStorageImpl.kt +++ b/android/src/main/java/com/rudderstack/android/storage/AndroidStorageImpl.kt @@ -33,6 +33,8 @@ class AndroidStorageImpl( private val storageExecutor: ExecutorService = Executors.newSingleThreadExecutor() ) : AndroidStorage { + override lateinit var analytics: Analytics + private var logger: Logger? = null private val dbName get() = "rs_persistence_$writeKey" private var jsonAdapter: JsonAdapter? = null @@ -71,6 +73,12 @@ class AndroidStorageImpl( private var rudderDatabase: RudderDatabase? = null + override fun setup(analytics: Analytics) { + super.setup(analytics) + initDb(analytics) + preferenceManager = RudderPreferenceManager(application, writeKey) + } + //message table listener private val _messageDataListener = object : Dao.DataChangeListener { override fun onDataInserted(inserted: List) { @@ -277,7 +285,7 @@ class AndroidStorageImpl( override val startupQueue: List get() = startupQ override val isOptedOut: Boolean - get() = preferenceManager?.optStatus?:false + get() = preferenceManager?.optStatus ?: false override val optOutTime: Long get() = _optOutTime.get() override val optInTime: Long @@ -321,7 +329,7 @@ class AndroidStorageImpl( override val v1AdvertisingId: String? get() = preferenceManager?.v1AdvertisingId override val trackAutoSession: Boolean - get() = preferenceManager?.trackAutoSession?: false + get() = preferenceManager?.trackAutoSession ?: false override val build: Int? get() = preferenceManager?.build override val v1Build: Int? @@ -402,8 +410,12 @@ class AndroidStorageImpl( } override fun migrateV1StorageToV2Sync(): Boolean { - return migrateV1MessagesToV2Database(application, rudderDatabase?:return false, - jsonAdapter?:return false, logger) + return migrateV1MessagesToV2Database( + application, + rudderDatabase ?: return false, + jsonAdapter ?: return false, + logger + ) } override fun migrateV1StorageToV2(callback: (Boolean) -> Unit) { @@ -442,11 +454,6 @@ class AndroidStorageImpl( override val libraryOsVersion: String get() = Build.VERSION.SDK_INT.toString() - override fun setup(analytics: Analytics) { - initDb(analytics) - preferenceManager = RudderPreferenceManager(application, writeKey) - } - private val Iterable.entities get() = map { it.entity diff --git a/core/src/main/java/com/rudderstack/core/BasicStorageImpl.kt b/core/src/main/java/com/rudderstack/core/BasicStorageImpl.kt index f0b46754..4490f611 100644 --- a/core/src/main/java/com/rudderstack/core/BasicStorageImpl.kt +++ b/core/src/main/java/com/rudderstack/core/BasicStorageImpl.kt @@ -3,9 +3,16 @@ package com.rudderstack.core import com.rudderstack.core.models.IdentifyTraits import com.rudderstack.core.models.Message import com.rudderstack.core.models.RudderServerConfig -import java.io.* +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.ObjectInputStream +import java.io.ObjectOutputStream import java.lang.ref.WeakReference -import java.util.* +import java.util.LinkedList +import java.util.Properties +import java.util.Queue import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.atomic.AtomicReference @@ -22,6 +29,8 @@ class BasicStorageImpl @JvmOverloads constructor( */ private val queue: Queue = LinkedBlockingQueue(), ) : Storage { + + override lateinit var analytics: Analytics private var configurationRef = AtomicReference(null) private val logger @@ -259,10 +268,6 @@ class BasicStorageImpl @JvmOverloads constructor( override val libraryOsVersion: String get() = libDetails[LIB_KEY_OS_VERSION] ?: "" - override fun setup(analytics: Analytics) { - //no-op - } - override fun updateConfiguration(configuration: Configuration) { configurationRef.set(configuration) } @@ -278,7 +283,5 @@ class BasicStorageImpl @JvmOverloads constructor( } } - } - } diff --git a/core/src/main/java/com/rudderstack/core/DestinationPlugin.kt b/core/src/main/java/com/rudderstack/core/DestinationPlugin.kt index b86c5da7..66de1f63 100644 --- a/core/src/main/java/com/rudderstack/core/DestinationPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/DestinationPlugin.kt @@ -60,7 +60,7 @@ interface DestinationPlugin : Plugin { * These plugins only act on the main-plugin that it is added to. * */ - fun interface DestinationInterceptor : Plugin + interface DestinationInterceptor : Plugin /** * Called when flush is triggered. @@ -128,6 +128,7 @@ abstract class BaseDestinationPlugin(override val name: String) : Destination crossinline block: (chain: Plugin.Chain) -> Message ): BaseDestinationPlugin = object : BaseDestinationPlugin(name) { + override lateinit var analytics: Analytics override fun intercept(chain: Plugin.Chain): Message { return block(chain) } diff --git a/core/src/main/java/com/rudderstack/core/InfrastructurePlugin.kt b/core/src/main/java/com/rudderstack/core/InfrastructurePlugin.kt index db4c52ce..61587f2e 100644 --- a/core/src/main/java/com/rudderstack/core/InfrastructurePlugin.kt +++ b/core/src/main/java/com/rudderstack/core/InfrastructurePlugin.kt @@ -9,8 +9,14 @@ import com.rudderstack.core.models.RudderServerConfig * */ interface InfrastructurePlugin { - fun setup(analytics: Analytics) - fun shutdown() + + var analytics: Analytics + + fun setup(analytics: Analytics) { + this.analytics = analytics + } + + fun shutdown() {} fun updateConfiguration(configuration: Configuration) { //optional method } diff --git a/core/src/main/java/com/rudderstack/core/Plugin.kt b/core/src/main/java/com/rudderstack/core/Plugin.kt index f2e6b735..93402ba6 100644 --- a/core/src/main/java/com/rudderstack/core/Plugin.kt +++ b/core/src/main/java/com/rudderstack/core/Plugin.kt @@ -8,7 +8,14 @@ import com.rudderstack.core.models.RudderServerConfig * responses coming back in. Typically plugins transforms or logs the data sent over to destinations. * */ -fun interface Plugin { +interface Plugin { + + var analytics: Analytics + + fun setup(analytics: Analytics) { + this.analytics = analytics + } + /** * Joins ("chains") the plugins for a particular message * @@ -83,13 +90,6 @@ fun interface Plugin { }*/ fun intercept(chain: Chain): Message - /** - * Setup code for this plugin - * Helps in changing settings, etc. - * @param analytics The analytics object this plugin is added to. - */ - fun setup(analytics: Analytics) {} - /** * Called when settings is updated * @@ -110,6 +110,4 @@ fun interface Plugin { * */ fun reset() {} - - } diff --git a/core/src/main/java/com/rudderstack/core/compat/BaseDestinationPluginCompat.java b/core/src/main/java/com/rudderstack/core/compat/BaseDestinationPluginCompat.java index ce0dd097..9f5a0dfe 100644 --- a/core/src/main/java/com/rudderstack/core/compat/BaseDestinationPluginCompat.java +++ b/core/src/main/java/com/rudderstack/core/compat/BaseDestinationPluginCompat.java @@ -74,5 +74,15 @@ public void onShutDown() { public void reset() { } + @NotNull + @Override + public Analytics getAnalytics() { + return null; //we will figure this out later + } + + @Override + public void setAnalytics(@NotNull Analytics analytics) { + + } } } diff --git a/core/src/main/java/com/rudderstack/core/flushpolicy/CountBasedFlushPolicy.kt b/core/src/main/java/com/rudderstack/core/flushpolicy/CountBasedFlushPolicy.kt index 741c2007..626ae931 100644 --- a/core/src/main/java/com/rudderstack/core/flushpolicy/CountBasedFlushPolicy.kt +++ b/core/src/main/java/com/rudderstack/core/flushpolicy/CountBasedFlushPolicy.kt @@ -9,6 +9,9 @@ import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference class CountBasedFlushPolicy : FlushPolicy { + + override lateinit var analytics: Analytics + private var storage: Storage? = null set(value) { synchronized(this) { @@ -19,8 +22,6 @@ class CountBasedFlushPolicy : FlushPolicy { field } private var flushCall: AtomicReference Unit> = AtomicReference({}) - - private var _analyticsRef = AtomicReference(null) private val flushQSizeThreshold = AtomicInteger(-1) private val _isShutDown = AtomicBoolean(false) private val onDataChange = object : Storage.DataListener { @@ -28,7 +29,6 @@ class CountBasedFlushPolicy : FlushPolicy { if (_isShutDown.get()) return flushIfNeeded() } - override fun onDataDropped(messages: List, error: Throwable) { /** * We won't be considering dropped events here @@ -36,13 +36,18 @@ class CountBasedFlushPolicy : FlushPolicy { } } + override fun setup(analytics: Analytics) { + super.setup(analytics) + if (storage == analytics.storage) return + storage = analytics.storage + storage?.addMessageDataListener(onDataChange) + } + private fun flushIfNeeded() { storage?.getCount { val threshold = flushQSizeThreshold.get() if (!_isShutDown.get() && threshold in 1..it) { - _analyticsRef.get()?.let { - flushCall.get()(it) - } + flushCall.get()(analytics) } } } @@ -56,18 +61,10 @@ class CountBasedFlushPolicy : FlushPolicy { } override fun onRemoved() { - _analyticsRef.set(null) storage?.removeMessageDataListener(onDataChange) storage = null } - override fun setup(analytics: Analytics) { - _analyticsRef.set(analytics) - if (storage == analytics.storage) return - storage = analytics.storage - storage?.addMessageDataListener(onDataChange) - } - override fun updateConfiguration(configuration: Configuration) { if (configuration.flushQueueSize == flushQSizeThreshold.get()) return diff --git a/core/src/main/java/com/rudderstack/core/flushpolicy/FlushPolicy.kt b/core/src/main/java/com/rudderstack/core/flushpolicy/FlushPolicy.kt index 706c386c..b39d74f1 100644 --- a/core/src/main/java/com/rudderstack/core/flushpolicy/FlushPolicy.kt +++ b/core/src/main/java/com/rudderstack/core/flushpolicy/FlushPolicy.kt @@ -20,6 +20,5 @@ import com.rudderstack.core.InfrastructurePlugin interface FlushPolicy : InfrastructurePlugin { fun reschedule() fun onRemoved() - fun setFlush(flush: Analytics.() -> Unit) -} \ No newline at end of file +} diff --git a/core/src/main/java/com/rudderstack/core/flushpolicy/IntervalBasedFlushPolicy.kt b/core/src/main/java/com/rudderstack/core/flushpolicy/IntervalBasedFlushPolicy.kt index e5f7281d..2dd09594 100644 --- a/core/src/main/java/com/rudderstack/core/flushpolicy/IntervalBasedFlushPolicy.kt +++ b/core/src/main/java/com/rudderstack/core/flushpolicy/IntervalBasedFlushPolicy.kt @@ -16,7 +16,6 @@ package com.rudderstack.core.flushpolicy import com.rudderstack.core.Analytics import com.rudderstack.core.Configuration -import java.lang.ref.WeakReference import java.util.Timer import java.util.TimerTask import java.util.concurrent.atomic.AtomicBoolean @@ -24,22 +23,32 @@ import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicReference class IntervalBasedFlushPolicy : FlushPolicy { + + override lateinit var analytics: Analytics + private var thresholdCountDownTimer: Timer? = null - private var _analyticsRef = AtomicReference(null) - private var flushCall : AtomicReference Unit> = AtomicReference({}) + + private var flushCall: AtomicReference Unit> = AtomicReference({}) private val _isShutDown = AtomicBoolean(false) private var _currentFlushIntervalAtomic = AtomicLong(0L) private val _currentFlushInterval get() = _currentFlushIntervalAtomic.get() private var periodicTaskScheduler: TimerTask? = null + + override fun setup(analytics: Analytics) { + super.setup(analytics) + _isShutDown.set(false) + thresholdCountDownTimer = Timer(analytics.writeKey + "-IntervalBasedFlushPolicy") + + } + override fun reschedule() { if (_isShutDown.get()) return rescheduleTimer() } override fun onRemoved() { - _analyticsRef.set(null) periodicTaskScheduler?.cancel() thresholdCountDownTimer?.purge() periodicTaskScheduler = null @@ -57,12 +66,6 @@ class IntervalBasedFlushPolicy : FlushPolicy { } } - override fun setup(analytics: Analytics) { - _isShutDown.set(false) - thresholdCountDownTimer = Timer(analytics.writeKey + "-IntervalBasedFlushPolicy") - _analyticsRef.set(analytics) - } - private fun updateMaxFlush(maxFlushInterval: Long) { _currentFlushIntervalAtomic.set(maxFlushInterval) } @@ -90,10 +93,8 @@ class IntervalBasedFlushPolicy : FlushPolicy { private fun createFlushTimerTask() = object : TimerTask() { override fun run() { synchronized(this@IntervalBasedFlushPolicy) { - if(!_isShutDown.get()) - _analyticsRef.get()?.let { - flushCall.get()(it) - } + if (!_isShutDown.get()) + flushCall.get()(analytics) } } } diff --git a/core/src/main/java/com/rudderstack/core/internal/AnalyticsDelegate.kt b/core/src/main/java/com/rudderstack/core/internal/AnalyticsDelegate.kt index 1f9d1c07..bd2858c8 100644 --- a/core/src/main/java/com/rudderstack/core/internal/AnalyticsDelegate.kt +++ b/core/src/main/java/com/rudderstack/core/internal/AnalyticsDelegate.kt @@ -13,8 +13,8 @@ import com.rudderstack.core.LifecycleController import com.rudderstack.core.Logger import com.rudderstack.core.Plugin import com.rudderstack.core.RudderOption -import com.rudderstack.core.RudderUtils.getUTF8Length import com.rudderstack.core.RudderUtils.MAX_BATCH_SIZE +import com.rudderstack.core.RudderUtils.getUTF8Length import com.rudderstack.core.Storage import com.rudderstack.core.flushpolicy.CountBasedFlushPolicy import com.rudderstack.core.flushpolicy.IntervalBasedFlushPolicy @@ -26,8 +26,8 @@ import com.rudderstack.core.holder.retrieveState import com.rudderstack.core.internal.plugins.CoreInputsPlugin import com.rudderstack.core.internal.plugins.DestinationConfigurationPlugin import com.rudderstack.core.internal.plugins.EventFilteringPlugin -import com.rudderstack.core.internal.plugins.GDPRPlugin import com.rudderstack.core.internal.plugins.EventSizeFilterPlugin +import com.rudderstack.core.internal.plugins.GDPRPlugin import com.rudderstack.core.internal.plugins.RudderOptionPlugin import com.rudderstack.core.internal.plugins.StoragePlugin import com.rudderstack.core.internal.plugins.WakeupActionPlugin @@ -192,7 +192,7 @@ internal class AnalyticsDelegate( val newConfiguration = configurationScope(it) currentConfigurationState?.update(newConfiguration) - }?: logger.error(log = "Configuration not updated, since current configuration is null") + } ?: logger.error(log = "Configuration not updated, since current configuration is null") } @@ -233,16 +233,18 @@ internal class AnalyticsDelegate( if (plugins.isEmpty()) return plugins.forEach { if (it is DestinationPlugin<*>) { - _destinationPlugins += it - val newDestinationConfig = - currentDestinationConfigurationState?.value?.withIntegration( - it.name, it.isReady - ) ?: DestinationConfig(mapOf(it.name to it.isReady)) + _destinationPlugins += it + val newDestinationConfig = currentDestinationConfigurationState?.value?.withIntegration( + it.name, it.isReady + ) ?: DestinationConfig(mapOf(it.name to it.isReady)) currentDestinationConfigurationState?.update(newDestinationConfig) initDestinationPlugin(it) } else _customPlugins = _customPlugins + it + //startup _analytics?.apply { + _analytics?.logger?.debug(log = "========= setting plugin up $it =========") + Logger.LogLevel.DEBUG it.setup(this) } applyUpdateClosures(it) @@ -323,7 +325,7 @@ internal class AnalyticsDelegate( private fun generatePluginsWithOptions(options: RudderOption?): List { return synchronized(PLUGIN_LOCK) { _internalPreMessagePlugins + (options ?: currentConfiguration?.options - ?: RudderOption()).createPlugin() + _customPlugins + _internalPostCustomPlugins + _destinationPlugins + ?: RudderOption()).createPlugin() + _customPlugins + _internalPostCustomPlugins + _destinationPlugins }.toList() } @@ -364,11 +366,12 @@ internal class AnalyticsDelegate( } } } + private val _isFlushing = AtomicBoolean(false) override fun blockingFlush( ): Boolean { if (_isShutDown.get()) return false - if(!_isFlushing.compareAndSet(false, true)) return false + if (!_isFlushing.compareAndSet(false, true)) return false //inform plugins broadcastFlush() @@ -473,6 +476,7 @@ internal class AnalyticsDelegate( onDestinationReady(true, plugin) } } + private fun onDestinationReady(isReady: Boolean, plugin: DestinationPlugin<*>) { if (isReady) { storage.startupQueue?.forEach { @@ -486,7 +490,7 @@ internal class AnalyticsDelegate( ) } val newDestinationConfig = (currentDestinationConfigurationState?.value - ?: DestinationConfig()).withIntegration( + ?: DestinationConfig()).withIntegration( plugin.name, isReady ) currentDestinationConfigurationState?.update(newDestinationConfig) @@ -499,7 +503,7 @@ internal class AnalyticsDelegate( //remove from destination config, else all integrations ready won't be true anytime val newDestinationConfig = (currentDestinationConfigurationState?.value - ?: DestinationConfig()).removeIntegration(plugin.name) + ?: DestinationConfig()).removeIntegration(plugin.name) currentDestinationConfigurationState?.update(newDestinationConfig) } diff --git a/core/src/main/java/com/rudderstack/core/internal/ConfigDownloadServiceImpl.kt b/core/src/main/java/com/rudderstack/core/internal/ConfigDownloadServiceImpl.kt index aac037d3..0c01c251 100644 --- a/core/src/main/java/com/rudderstack/core/internal/ConfigDownloadServiceImpl.kt +++ b/core/src/main/java/com/rudderstack/core/internal/ConfigDownloadServiceImpl.kt @@ -7,7 +7,6 @@ import com.rudderstack.core.models.RudderServerConfig import com.rudderstack.web.HttpResponse import com.rudderstack.web.WebService import com.rudderstack.web.WebServiceFactory -import java.lang.ref.WeakReference import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.Future @@ -17,6 +16,9 @@ internal class ConfigDownloadServiceImpl @JvmOverloads constructor( private val writeKey: String, webService: WebService? = null, ) : ConfigDownloadService { + + override lateinit var analytics: Analytics + private val downloadSequence = CopyOnWriteArrayList() private var listeners = CopyOnWriteArrayList() private val controlPlaneWebService: AtomicReference = @@ -34,20 +36,17 @@ internal class ConfigDownloadServiceImpl @JvmOverloads constructor( ) } - private var analyticsRef: WeakReference? = null - private var ongoingConfigFuture: Future>? = null private var lastRudderServerConfig: RudderServerConfig? = null private var lastErrorMsg: String? = null + override fun download( - callback: ( - success: Boolean, RudderServerConfig?, lastErrorMsg: String? - ) -> Unit + callback: (success: Boolean, RudderServerConfig?, lastErrorMsg: String?) -> Unit ) { currentConfiguration?.apply { networkExecutor.submit { with(sdkVerifyRetryStrategy) { - analyticsRef?.get()?.perform({ + analytics.perform({ ongoingConfigFuture = controlPlaneWebService.get()?.get( mapOf( "Content-Type" to "application/json", @@ -57,16 +56,16 @@ internal class ConfigDownloadServiceImpl @JvmOverloads constructor( encodedWriteKey ) ), mapOf( - "p" to storage.libraryPlatform, - "v" to storage.libraryVersion, - "bv" to storage.libraryOsVersion + "p" to this.storage.libraryPlatform, + "v" to this.storage.libraryVersion, + "bv" to this.storage.libraryOsVersion ), "sourceConfig", RudderServerConfig::class.java ) val response = ongoingConfigFuture?.get() lastRudderServerConfig = response?.body lastErrorMsg = response?.errorBody ?: response?.error?.message return@perform (ongoingConfigFuture?.get()?.status ?: -1) == 200 //TODO - - // if the status is excluded from retry 429 or 500-599 + // if the status is excluded from retry 429 or 500-599 }) { listeners.forEach { listener -> listener.onDownloaded(it) @@ -114,16 +113,8 @@ internal class ConfigDownloadServiceImpl @JvmOverloads constructor( encodedWriteKey.set(null) try { ongoingConfigFuture?.cancel(true) - } catch (ex: Exception) { // Ignore the exception } - analyticsRef = WeakReference(null) } - - override fun setup(analytics: Analytics) { - analyticsRef = WeakReference(analytics) - } - - } diff --git a/core/src/main/java/com/rudderstack/core/internal/DataUploadServiceImpl.kt b/core/src/main/java/com/rudderstack/core/internal/DataUploadServiceImpl.kt index 24a5a8c5..4402ae74 100644 --- a/core/src/main/java/com/rudderstack/core/internal/DataUploadServiceImpl.kt +++ b/core/src/main/java/com/rudderstack/core/internal/DataUploadServiceImpl.kt @@ -1,21 +1,26 @@ - package com.rudderstack.core.internal -import com.rudderstack.core.* +import com.rudderstack.core.Analytics +import com.rudderstack.core.Configuration +import com.rudderstack.core.DataUploadService +import com.rudderstack.core.RudderUtils import com.rudderstack.core.models.Message import com.rudderstack.rudderjsonadapter.RudderTypeAdapter import com.rudderstack.web.HttpInterceptor import com.rudderstack.web.HttpResponse import com.rudderstack.web.WebService import com.rudderstack.web.WebServiceFactory -import java.util.* +import java.util.Locale import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference internal class DataUploadServiceImpl @JvmOverloads constructor( private val writeKey: String, - webService: WebService? = null + webService: WebService? = null, ) : DataUploadService { + + override lateinit var analytics: Analytics + private val encodedWriteKey: AtomicReference = AtomicReference() private val webService: AtomicReference = AtomicReference(webService) private val currentConfigurationAtomic = AtomicReference() @@ -78,10 +83,7 @@ internal class DataUploadServiceImpl @JvmOverloads constructor( } - - override fun uploadSync( - data: List, extraInfo: Map? - ): HttpResponse? { + override fun uploadSync(data: List, extraInfo: Map?): HttpResponse? { if (_isPaused.get()) return null return currentConfiguration?.let { config -> val batchBody = config.createBatchBody(data, extraInfo) @@ -94,12 +96,13 @@ internal class DataUploadServiceImpl @JvmOverloads constructor( )?.get() } } + private fun Configuration.createBatchBody( data: List, extraInfo: Map? - ) : String?{ + ): String? { val sentAt = RudderUtils.timeStamp - return mapOf( + return mapOf( "sentAt" to sentAt, "batch" to data.map { it.sentAt = sentAt it @@ -110,9 +113,6 @@ internal class DataUploadServiceImpl @JvmOverloads constructor( jsonAdapter.writeToJson(it, object : RudderTypeAdapter>() {}) } } - override fun setup(analytics: Analytics) { - // No-op - } override fun updateConfiguration(configuration: Configuration) { encodedWriteKey.set(configuration.base64Generator.generateBase64(writeKey)) @@ -126,7 +126,6 @@ internal class DataUploadServiceImpl @JvmOverloads constructor( override fun resume() { _isPaused.set(false) - } override fun shutdown() { diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/CoreInputsPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/CoreInputsPlugin.kt index 9b29010e..8dc180d7 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/CoreInputsPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/CoreInputsPlugin.kt @@ -10,23 +10,18 @@ private const val LIBRARY_KEY = "library" /** * Plugin to add the library details to context object of payload. */ -class CoreInputsPlugin : Plugin{ +class CoreInputsPlugin : Plugin { + override lateinit var analytics: Analytics + private val Storage.libraryContextPair get() = LIBRARY_KEY to mapOf("name" to libraryName, "version" to libraryVersion) - private var storage: Storage?= null - override fun setup(analytics: Analytics) { - storage = analytics.storage - } + override fun intercept(chain: Plugin.Chain): Message { val message = chain.message() - val context = storage?.let { - storage -> message.context?.let { it + storage.libraryContextPair }?: mapOf(storage.libraryContextPair) - }?: return chain.proceed(message) + val context = analytics.storage.let { storage -> + message.context?.let { it + storage.libraryContextPair } ?: mapOf(storage.libraryContextPair) + } return chain.proceed(message.copy(context = context)) } - override fun onShutDown() { - storage = null - } - } diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPlugin.kt index 606859c3..7a832545 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPlugin.kt @@ -1,5 +1,6 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.DestinationPlugin import com.rudderstack.core.Plugin import com.rudderstack.core.models.Message @@ -18,7 +19,9 @@ import com.rudderstack.core.models.RudderServerConfig * plugins that are disabled in destination config are filtered out. */ internal class DestinationConfigurationPlugin : Plugin { - private var _notAllowedDestinations : Set = setOf() + override lateinit var analytics: Analytics + + private var _notAllowedDestinations: Set = setOf() private var _isConfigUpdated = false override fun intercept(chain: Plugin.Chain): Message { val msg = chain.message() @@ -26,7 +29,7 @@ internal class DestinationConfigurationPlugin : Plugin { //either not a destination plugin or is allowed it !is DestinationPlugin<*> || (_isConfigUpdated && it.name !in _notAllowedDestinations) } - return if (validPlugins.isNotEmpty()) { + return if (validPlugins.isNotEmpty()) { return chain.with(validPlugins).proceed(msg) } else chain.proceed(msg) @@ -37,7 +40,7 @@ internal class DestinationConfigurationPlugin : Plugin { _notAllowedDestinations = config.source?.destinations?.filter { !it.isDestinationEnabled }?.map { - it.destinationDefinition?.definitionName?:it.destinationName?: "" - }?.toHashSet()?: setOf() + it.destinationDefinition?.definitionName ?: it.destinationName ?: "" + }?.toHashSet() ?: setOf() } } diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/EventFilteringPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/EventFilteringPlugin.kt index 65fc9dc3..3e18c71e 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/EventFilteringPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/EventFilteringPlugin.kt @@ -1,5 +1,6 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.DestinationPlugin import com.rudderstack.core.Plugin import com.rudderstack.core.models.Message @@ -17,8 +18,10 @@ private const val WHITELISTED_EVENTS = "whitelistedEvents" private const val BLACKLISTED_EVENTS = "blacklistedEvents" private const val EVENT_FILTERING_OPTION = "eventFilteringOption" private const val EVENT_NAME = "eventName" + class EventFilteringPlugin : Plugin { + override lateinit var analytics: Analytics //map of (destination definition name, DestinationEventFilteringConfig) private var filteredEventsMap = ConcurrentHashMap() diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/EventSizeFilterPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/EventSizeFilterPlugin.kt index f696a7cd..a49766af 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/EventSizeFilterPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/EventSizeFilterPlugin.kt @@ -1,5 +1,6 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.Configuration import com.rudderstack.core.Plugin import com.rudderstack.core.RudderUtils.MAX_EVENT_SIZE @@ -12,6 +13,8 @@ import java.util.concurrent.atomic.AtomicReference * A plugin to filter out events that exceed the maximum size limit. */ class EventSizeFilterPlugin : Plugin { + + override lateinit var analytics: Analytics private val currentConfigurationAtomic = AtomicReference() private val currentConfiguration diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/GDPRPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/GDPRPlugin.kt index 73311cb5..1d07c740 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/GDPRPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/GDPRPlugin.kt @@ -10,18 +10,13 @@ import com.rudderstack.core.models.Message */ internal class GDPRPlugin : Plugin { - private var _analytics: Analytics? = null + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { - val isOptOut = _analytics?.storage?.isOptedOut ?: false - return if (isOptOut) { + return if (analytics.storage.isOptedOut) { chain.message() } else { chain.proceed(chain.message()) } } - - override fun setup(analytics: Analytics) { - super.setup(analytics) - _analytics = analytics - } } diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/RudderOptionPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/RudderOptionPlugin.kt index eb9a11c0..265674eb 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/RudderOptionPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/RudderOptionPlugin.kt @@ -1,10 +1,13 @@ package com.rudderstack.core.internal.plugins -import com.rudderstack.core.models.* +import com.rudderstack.core.Analytics import com.rudderstack.core.DestinationPlugin import com.rudderstack.core.Plugin import com.rudderstack.core.RudderOption import com.rudderstack.core.minusWrtKeys +import com.rudderstack.core.models.Message +import com.rudderstack.core.models.MessageContext +import com.rudderstack.core.models.externalIds import com.rudderstack.core.models.updateWith /** @@ -22,6 +25,8 @@ import com.rudderstack.core.models.updateWith */ internal class RudderOptionPlugin(private val options: RudderOption) : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { val msg = chain.message().let { oldMsg -> oldMsg.copy( @@ -62,7 +67,6 @@ internal class RudderOptionPlugin(private val options: RudderOption) : Plugin { private fun validIntegrations(): Map { return options.integrations.ifEmpty { mapOf("All" to true) } - } private fun Message.updateContext(options: RudderOption): MessageContext? { diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/StoragePlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/StoragePlugin.kt index a3dc7672..0f16f351 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/StoragePlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/StoragePlugin.kt @@ -3,7 +3,6 @@ package com.rudderstack.core.internal.plugins import com.rudderstack.core.Analytics import com.rudderstack.core.Plugin import com.rudderstack.core.models.Message -import java.lang.ref.WeakReference /** * Adds [Message] to repository for further processing. @@ -12,19 +11,13 @@ import java.lang.ref.WeakReference * */ internal class StoragePlugin : Plugin { - private var _analytics: WeakReference = WeakReference(null) - override fun setup(analytics: Analytics) { - _analytics = WeakReference(analytics) - } + + override lateinit var analytics: Analytics override fun intercept(chain: Plugin.Chain): Message { val message = chain.message() - _analytics.get()?.storage?.saveMessage(message.copy()) - + analytics.storage.saveMessage(message.copy()) return chain.proceed(message) } - override fun onShutDown() { - _analytics = WeakReference(null) - } } diff --git a/core/src/main/java/com/rudderstack/core/internal/plugins/WakeupActionPlugin.kt b/core/src/main/java/com/rudderstack/core/internal/plugins/WakeupActionPlugin.kt index c0ac9613..78c0d790 100644 --- a/core/src/main/java/com/rudderstack/core/internal/plugins/WakeupActionPlugin.kt +++ b/core/src/main/java/com/rudderstack/core/internal/plugins/WakeupActionPlugin.kt @@ -6,33 +6,25 @@ import com.rudderstack.core.Plugin import com.rudderstack.core.holder.retrieveState import com.rudderstack.core.internal.states.DestinationConfigState import com.rudderstack.core.models.Message -import java.lang.ref.WeakReference /** * Must be added prior to destination plugins. * Will store messages till all factories are ready * After that reiterate the messages to the plugins */ -internal class WakeupActionPlugin( -// private val destConfigState: State = DestinationConfigState -) : Plugin { +internal class WakeupActionPlugin : Plugin { - private var _analytics: WeakReference = WeakReference(null) - override fun setup(analytics: Analytics) { - _analytics = WeakReference(analytics) - } + override lateinit var analytics: Analytics - private val storage - get() = _analytics.get()?.storage private val Analytics.destinationConfigState: DestinationConfigState? get() = retrieveState() override fun intercept(chain: Plugin.Chain): Message { - val destinationConfig = _analytics.get()?.destinationConfigState?.value + val destinationConfig = analytics.destinationConfigState?.value val forwardChain = - if (destinationConfig == null || !destinationConfig.allIntegrationsReady || storage?.startupQueue?.isNotEmpty() == true) { - storage?.saveStartupMessageInQueue(chain.message()) + if (destinationConfig == null || !destinationConfig.allIntegrationsReady || analytics.storage.startupQueue.isNotEmpty()) { + analytics.storage.saveStartupMessageInQueue(chain.message()) //remove all destination plugins that are not ready, for others the message flow is normal val validPlugins = chain.plugins.toMutableList().filterNot { it is DestinationPlugin<*> && destinationConfig?.isIntegrationReady(it.name) != true @@ -42,8 +34,4 @@ internal class WakeupActionPlugin( return forwardChain.proceed(chain.message()) } - - override fun onShutDown() { - _analytics = WeakReference(null) - } } diff --git a/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt b/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt index ff3068ac..6a592d08 100644 --- a/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt +++ b/core/src/test/java/com/rudderstack/core/AnalyticsTest.kt @@ -521,20 +521,21 @@ abstract class AnalyticsTest { } val isDone = AtomicBoolean(false) var msgCounter = 1 - val assertionPlugin = Plugin { - val msg = it.message() - assertThat( - msg, allOf( - Matchers.isA(TrackMessage::class.java), - hasProperty("eventName", `is`("event:${msgCounter++}")) + val assertPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat( + chain.message(), allOf( + Matchers.isA(TrackMessage::class.java), + hasProperty("eventName", `is`("event:${msgCounter++}")) + ) ) - ) - Thread.sleep(20L) //a minor delay - if (events.size < msgCounter) isDone.set(true) - it.proceed(it.message()) + Thread.sleep(20L) //a minor delay + if (events.size < msgCounter) isDone.set(true) + return chain.proceed(chain.message()) + } } - - analytics.addPlugin(assertionPlugin) + analytics.addPlugin(assertPlugin) for (i in events) { analytics.track(i) } @@ -784,11 +785,15 @@ abstract class AnalyticsTest { fun `test custom plugin`() { println("running test test custom plugin") val isDone = AtomicBoolean(false) - val customPlugin = Plugin { - println("inside custom plugin") - isDone.set(true) - it.proceed(it.message()) + val customPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + println("inside custom plugin") + isDone.set(true) + return chain.proceed(chain.message()) + } } + val waitUntil = AtomicBoolean(false) analytics.addCallback(object : Callback { override fun success(message: Message?) { diff --git a/core/src/test/java/com/rudderstack/core/internal/plugins/CoreInputsPluginTest.kt b/core/src/test/java/com/rudderstack/core/internal/plugins/CoreInputsPluginTest.kt index 96b7a9a9..1af9669a 100644 --- a/core/src/test/java/com/rudderstack/core/internal/plugins/CoreInputsPluginTest.kt +++ b/core/src/test/java/com/rudderstack/core/internal/plugins/CoreInputsPluginTest.kt @@ -42,25 +42,6 @@ class CoreInputsPluginTest { analytics.shutdown() } - @Test - fun `intercept method proceeds without modification when storage is null`() { - // Arrange - val mockChain = mock() - val mockMessage = TrackMessage.create("ev_name", RudderUtils.timeStamp) - `when`(mockChain.message()).thenReturn(mockMessage) - whenever(mockChain.proceed(any())) doAnswer { - it.getArgument(0) - } - - // Act - val result = coreInputsPlugin.intercept(mockChain) - - // Assert - assertThat(result, `is`(mockMessage)) - verify(mockChain).proceed(mockMessage) - } - - @Test fun `intercept method adds library context to message context when storage is not null`() { // Arrange diff --git a/core/src/test/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPluginTest.kt b/core/src/test/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPluginTest.kt index 522d2233..ab0663d6 100644 --- a/core/src/test/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPluginTest.kt +++ b/core/src/test/java/com/rudderstack/core/internal/plugins/DestinationConfigurationPluginTest.kt @@ -1,10 +1,12 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.BaseDestinationPlugin import com.rudderstack.core.DestinationPlugin import com.rudderstack.core.Plugin import com.rudderstack.core.RudderUtils import com.rudderstack.core.internal.CentralPluginChain +import com.rudderstack.core.models.Message import com.rudderstack.core.models.RudderServerConfig import com.rudderstack.core.models.TrackMessage import org.hamcrest.MatcherAssert.assertThat @@ -91,19 +93,18 @@ class DestinationConfigurationPluginTest { val centralPluginChain = defaultPluginChain!!.copy( plugins = defaultPluginChain!!.plugins.toMutableList().also { //after destination config plugin - it.add(0, Plugin { - //after processing the chain should be devoid of d-2 - assertThat( - it.plugins, allOf( - Matchers.hasItems(*(destinations.toMutableList().also { - it.removeIf { - it is DestinationPlugin<*> && it.name == "d-2" - } - }.toTypedArray())), - everyItem(not(destinations[1]/*isIn(shouldNotBeInList)*/)) + it.add(0, object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat( + chain.plugins, allOf( + Matchers.hasItems(*(destinations.toMutableList().also { + it.removeIf { it is DestinationPlugin<*> && it.name == "d-2" } + }.toTypedArray())), everyItem(not(destinations[1]/*isIn(shouldNotBeInList)*/)) + ) ) - ) - it.proceed(it.message()) + return chain.proceed(chain.message()) + } }) } ) @@ -117,16 +118,17 @@ class DestinationConfigurationPluginTest { val centralPluginChain = defaultPluginChain!!.copy( plugins = defaultPluginChain!!.plugins.toMutableList().also { //after destination config plugin - it.add(0, Plugin { - //after processing the chain should be devoid of d-2 - assertThat( - it.plugins, allOf( - iterableWithSize(1), //the test plugin - //check there should be no destination plugin - everyItem(not(isA(DestinationPlugin::class.java))) + it.add(0, object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat( + chain.plugins, allOf( + iterableWithSize(1), + everyItem(not(isA(DestinationPlugin::class.java))) + ) ) - ) - it.proceed(it.message()) + return chain.proceed(chain.message()) + } }) } ) diff --git a/core/src/test/java/com/rudderstack/core/internal/plugins/EventSizeFilterPluginTest.kt b/core/src/test/java/com/rudderstack/core/internal/plugins/EventSizeFilterPluginTest.kt index e9dae4fc..4f859489 100644 --- a/core/src/test/java/com/rudderstack/core/internal/plugins/EventSizeFilterPluginTest.kt +++ b/core/src/test/java/com/rudderstack/core/internal/plugins/EventSizeFilterPluginTest.kt @@ -1,14 +1,15 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.Configuration import com.rudderstack.core.Plugin import com.rudderstack.core.RudderUtils import com.rudderstack.core.RudderUtils.MAX_EVENT_SIZE import com.rudderstack.core.RudderUtils.getUTF8Length import com.rudderstack.core.internal.CentralPluginChain -import com.rudderstack.gsonrudderadapter.GsonAdapter import com.rudderstack.core.models.Message import com.rudderstack.core.models.TrackMessage +import com.rudderstack.gsonrudderadapter.GsonAdapter import org.junit.Test class EventSizeFilterPluginTest { @@ -19,9 +20,12 @@ class EventSizeFilterPluginTest { @Test fun `given event size does not exceed the maximum size, then the next plugin in the chain should be called`() { var isCalled = false - val testPlugin = Plugin { - isCalled = true - it.proceed(it.message()) + val testPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + isCalled = true + return chain.proceed(chain.message()) + } } val message = getMessageUnderMaxSize() val eventSizeFilterTestChain = CentralPluginChain( @@ -39,9 +43,12 @@ class EventSizeFilterPluginTest { @Test fun `given event size exceeds the maximum size, then the next plugin in the chain should not be called`() { var isCalled = false - val testPlugin = Plugin { - isCalled = true - it.proceed(it.message()) + val testPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + isCalled = true + return chain.proceed(chain.message()) + } } val message = getMessageOverMaxSize() val eventSizeFilterTestChain = CentralPluginChain( diff --git a/core/src/test/java/com/rudderstack/core/internal/plugins/GDPRPluginTest.kt b/core/src/test/java/com/rudderstack/core/internal/plugins/GDPRPluginTest.kt index 3b4e1783..db5ff20b 100644 --- a/core/src/test/java/com/rudderstack/core/internal/plugins/GDPRPluginTest.kt +++ b/core/src/test/java/com/rudderstack/core/internal/plugins/GDPRPluginTest.kt @@ -4,6 +4,7 @@ import com.rudderstack.core.Analytics import com.rudderstack.core.Plugin import com.rudderstack.core.RudderUtils import com.rudderstack.core.internal.CentralPluginChain +import com.rudderstack.core.models.Message import com.rudderstack.core.models.TrackMessage import io.mockk.every import io.mockk.mockk @@ -30,10 +31,12 @@ class GDPRPluginTest { fun `test gdpr with opt out`() { val analytics = mockk(relaxed = true) every { analytics.storage.isOptedOut } returns true - val testPluginForOptOut = Plugin { - //should not be called - assert(false) - it.proceed(it.message()) + val testPluginForOptOut = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assert(false) + return chain.proceed(chain.message()) + } } val optOutTestChain = CentralPluginChain( message, listOf(gdprPlugin, testPluginForOptOut), originalMessage = message @@ -51,11 +54,14 @@ class GDPRPluginTest { every { analytics.storage.isOptedOut } returns false var isCalled = false - val testPluginForOptIn = Plugin { - //should be called - isCalled = true - it.proceed(it.message()) + val testPluginForOptIn = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + isCalled = true + return chain.proceed(chain.message()) + } } + val optInTestChain = CentralPluginChain( message, listOf(gdprPlugin, testPluginForOptIn), originalMessage = message diff --git a/core/src/test/java/com/rudderstack/core/internal/plugins/RudderOptionPluginTest.kt b/core/src/test/java/com/rudderstack/core/internal/plugins/RudderOptionPluginTest.kt index b7aae00b..fb9ae613 100644 --- a/core/src/test/java/com/rudderstack/core/internal/plugins/RudderOptionPluginTest.kt +++ b/core/src/test/java/com/rudderstack/core/internal/plugins/RudderOptionPluginTest.kt @@ -1,10 +1,12 @@ package com.rudderstack.core.internal.plugins +import com.rudderstack.core.Analytics import com.rudderstack.core.BaseDestinationPlugin import com.rudderstack.core.Plugin import com.rudderstack.core.RudderOption import com.rudderstack.core.RudderUtils import com.rudderstack.core.internal.CentralPluginChain +import com.rudderstack.core.models.Message import com.rudderstack.core.models.TrackMessage import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf @@ -45,16 +47,14 @@ class RudderOptionPluginTest { @Test fun `test all true for empty integrations`() { //assertion plugin - val assertPlugin = Plugin { - //must contain all plugins - assertThat( - it.plugins, allOf( - iterableWithSize(5), - hasItems(dest1, dest2, dest3) - ) - ) - return@Plugin it.proceed(it.message()) + val assertPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat(chain.plugins, allOf(iterableWithSize(5), hasItems(dest1, dest2, dest3))) + return chain.proceed(chain.message()) + } } + val chain = CentralPluginChain( message, listOf( RudderOptionPlugin(RudderOption()), assertPlugin, dest1, dest2, dest3 @@ -66,15 +66,12 @@ class RudderOptionPluginTest { @Test fun `test all false for integrations`() { //assertion plugin - val assertPlugin = Plugin { - //must contain all plugins - assertThat( - it.plugins, allOf( - iterableWithSize(2), - everyItem(not(`in`(arrayOf(dest1, dest2, dest3)))) - ) - ) - return@Plugin it.proceed(it.message()) + val assertPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat(chain.plugins, allOf(iterableWithSize(2), everyItem(not(`in`(arrayOf(dest1, dest2, dest3)))))) + return chain.proceed(chain.message()) + } } val chain = CentralPluginChain( message, listOf( @@ -90,16 +87,18 @@ class RudderOptionPluginTest { @Test fun `test custom integrations with false`() { //assertion plugin - val assertPlugin = Plugin { - //must contain all plugins - assertThat( - it.plugins, allOf( - iterableWithSize(3), - everyItem(not(`in`(arrayOf(dest2, dest3)))), - hasItem(dest1) + val assertPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat( + chain.plugins, allOf( + iterableWithSize(3), + everyItem(not(`in`(arrayOf(dest2, dest3)))), + hasItem(dest1) + ) ) - ) - return@Plugin it.proceed(it.message()) + return chain.proceed(chain.message()) + } } val chain = CentralPluginChain( message, listOf( @@ -116,16 +115,14 @@ class RudderOptionPluginTest { @Test fun `test custom integrations with true`() { //assertion plugin - val assertPlugin = Plugin { - //must contain all plugins - assertThat( - it.plugins, allOf( - iterableWithSize(3), - everyItem(not(`in`(arrayOf(dest1, dest3)))), - hasItem(dest2) + val assertPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + assertThat( + chain.plugins, allOf(iterableWithSize(3), everyItem(not(`in`(arrayOf(dest1, dest3)))), hasItem(dest2)) ) - ) - return@Plugin it.proceed(it.message()) + return chain.proceed(chain.message()) + } } val chain = CentralPluginChain( message, listOf( diff --git a/libs/navigationplugin/src/main/java/com/rudderstack/android/navigationplugin/internal/NavigationPlugin.kt b/libs/navigationplugin/src/main/java/com/rudderstack/android/navigationplugin/internal/NavigationPlugin.kt index 680776f7..e921f159 100644 --- a/libs/navigationplugin/src/main/java/com/rudderstack/android/navigationplugin/internal/NavigationPlugin.kt +++ b/libs/navigationplugin/src/main/java/com/rudderstack/android/navigationplugin/internal/NavigationPlugin.kt @@ -23,23 +23,20 @@ import com.rudderstack.core.Analytics import com.rudderstack.core.InfrastructurePlugin import com.rudderstack.core.State -internal class NavigationPlugin(private val navControllerState: State>) : - InfrastructurePlugin, NavController.OnDestinationChangedListener { - private var analytics: Analytics? = null +internal class NavigationPlugin( + private val navControllerState: State> +) : InfrastructurePlugin, NavController.OnDestinationChangedListener { + + override lateinit var analytics: Analytics + private var currentNavControllers: Collection? = null private val currentConfig - get() = analytics?.currentConfigurationAndroid + get() = analytics?.currentConfigurationAndroid override fun setup(analytics: Analytics) { - this.analytics = analytics - if (currentConfig?.trackLifecycleEvents == true - && currentConfig?.recordScreenViews == true) - subscribeToNavControllerState() - } - - private fun subscribeToNavControllerState() { - navControllerState.subscribe { newValue, _ -> - updateNavControllers(newValue) + super.setup(analytics) + if (currentConfig?.trackLifecycleEvents == true && currentConfig?.recordScreenViews == true) { + navControllerState.subscribe { newValue, _ -> updateNavControllers(newValue) } } } @@ -76,10 +73,11 @@ internal class NavigationPlugin(private val navControllerState: State { @@ -92,14 +90,17 @@ internal class NavigationPlugin(private val navControllerState: State ): Map = arguments?.let { bundle -> - argumentKeys.associateWith { bundle.get(it) } - }?.filter { it.value != null }?.mapValues { it.value!! } ?: mapOf() + argumentKeys.associateWith { bundle.get(it) } + }?.filter { it.value != null }?.mapValues { it.value!! } ?: mapOf() private fun broadcastScreenChange( - screenName: String, properties: Map? + screenName: String, + properties: Map? ) { - analytics?.applyInfrastructureClosure { + analytics.applyInfrastructureClosure { if (this is LifecycleListenerPlugin) { this.onScreenChange(screenName, properties) } } - analytics?.applyMessageClosure { + analytics.applyMessageClosure { if (this is LifecycleListenerPlugin) { this.onScreenChange(screenName, properties) } } } -} \ No newline at end of file +} diff --git a/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt index 6695634f..5e1e5439 100644 --- a/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt +++ b/libs/sync/src/main/java/com/rudderstack/android/sync/WorkerManagerPlugin.kt @@ -1,30 +1,19 @@ -/* - * Creator: Debanjan Chatterjee on 23/11/23, 6:20 pm Last modified: 23/11/23, 6:20 pm - * Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain a - * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - package com.rudderstack.android.sync import android.app.Application -import com.rudderstack.android.utilities.currentConfigurationAndroid import com.rudderstack.android.sync.internal.registerWorkManager import com.rudderstack.android.sync.internal.unregisterWorkManager +import com.rudderstack.android.utilities.currentConfigurationAndroid import com.rudderstack.core.Analytics import com.rudderstack.core.InfrastructurePlugin abstract class WorkerManagerPlugin : InfrastructurePlugin { - private var application: Application?= null + + private var application: Application? = null private var analyticsIdentifier: String? = null + override fun setup(analytics: Analytics) { + super.setup(analytics) analyticsIdentifier = analytics.writeKey val currentConfig = analytics.currentConfigurationAndroid if (currentConfig?.isPeriodicFlushEnabled != true) { @@ -40,6 +29,7 @@ abstract class WorkerManagerPlugin : InfrastructurePlugin { ) } } + override fun shutdown() { application?.unregisterWorkManager(analyticsIdentifier ?: return) analyticsIdentifier = null @@ -52,5 +42,4 @@ abstract class WorkerManagerPlugin : InfrastructurePlugin { * with garbage collector */ abstract val workManagerAnalyticsFactoryClassName: Class - } diff --git a/libs/test-common/src/main/java/com/vagabond/testcommon/MockConfigDownloadService.kt b/libs/test-common/src/main/java/com/vagabond/testcommon/MockConfigDownloadService.kt index 4d16f1fb..d98140c9 100644 --- a/libs/test-common/src/main/java/com/vagabond/testcommon/MockConfigDownloadService.kt +++ b/libs/test-common/src/main/java/com/vagabond/testcommon/MockConfigDownloadService.kt @@ -12,6 +12,7 @@ class MockConfigDownloadService( ) ) : ConfigDownloadService { + override lateinit var analytics: Analytics override fun download(callback: (success: Boolean, RudderServerConfig?, lastErrorMsg: String?) -> Unit) { callback(mockConfigDownloadSuccess, mockConfig, mockLastErrorMsg) @@ -25,10 +26,6 @@ class MockConfigDownloadService( // Not-required } - override fun setup(analytics: Analytics) { - // Not-required - } - override fun shutdown() { // Not-required } diff --git a/libs/test-common/src/main/java/com/vagabond/testcommon/TestAnalyticsProvider.kt b/libs/test-common/src/main/java/com/vagabond/testcommon/TestAnalyticsProvider.kt index c7c4038d..6c1de413 100644 --- a/libs/test-common/src/main/java/com/vagabond/testcommon/TestAnalyticsProvider.kt +++ b/libs/test-common/src/main/java/com/vagabond/testcommon/TestAnalyticsProvider.kt @@ -14,10 +14,13 @@ import com.rudderstack.rudderjsonadapter.JsonAdapter private const val DUMMY_WRITE_KEY = "DUMMY_WRITE_KEY" private var currentTestPlugin: Plugin? = null private var inputs = listOf() -val inputVerifyPlugin = Plugin { chain -> - chain.proceed(chain.message().also { - inputs += it.copy() - }) +val inputVerifyPlugin = object : Plugin { + override lateinit var analytics: Analytics + override fun intercept(chain: Plugin.Chain): Message { + return chain.proceed(chain.message().also { + inputs += it.copy() + }) + } } fun generateTestAnalytics(jsonAdapter: JsonAdapter): Analytics { diff --git a/libs/test-common/src/main/java/com/vagabond/testcommon/TestDataUploadService.kt b/libs/test-common/src/main/java/com/vagabond/testcommon/TestDataUploadService.kt index bce4866c..b0d4f1e6 100644 --- a/libs/test-common/src/main/java/com/vagabond/testcommon/TestDataUploadService.kt +++ b/libs/test-common/src/main/java/com/vagabond/testcommon/TestDataUploadService.kt @@ -6,11 +6,13 @@ import com.rudderstack.core.models.Message import com.rudderstack.web.HttpResponse class TestDataUploadService : DataUploadService { + + override lateinit var analytics: Analytics private var headers = mutableMapOf() var mockUploadStatus = 200 var mockUploadBody = "OK" - var errorBody : String? = null + var errorBody: String? = null var error: Throwable? = null override fun addHeaders(headers: Map) { this.headers += headers @@ -30,8 +32,6 @@ class TestDataUploadService : DataUploadService { return HttpResponse(mockUploadStatus, mockUploadBody, errorBody, error) } - override fun setup(analytics: Analytics) {} - override fun shutdown() { headers = mutableMapOf() } diff --git a/libs/test-common/src/main/java/com/vagabond/testcommon/VerificationStorage.kt b/libs/test-common/src/main/java/com/vagabond/testcommon/VerificationStorage.kt index c84f4f48..b22922b8 100644 --- a/libs/test-common/src/main/java/com/vagabond/testcommon/VerificationStorage.kt +++ b/libs/test-common/src/main/java/com/vagabond/testcommon/VerificationStorage.kt @@ -7,6 +7,7 @@ import com.rudderstack.core.models.RudderServerConfig class VerificationStorage : Storage { + override lateinit var analytics: Analytics private var storageQ = mutableListOf() private var _serverConfig: RudderServerConfig? = null override fun setStorageCapacity(storageCapacity: Int) { @@ -99,8 +100,4 @@ class VerificationStorage : Storage { override val libraryOsVersion: String get() = "13" - override fun setup(analytics: Analytics) { - /* No-op . */ - } - } diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt index 263a1b57..abf4870c 100644 --- a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/analytics/workmanger/SampleWorkManagerPlugin.kt @@ -16,8 +16,12 @@ package com.rudderstack.android.sampleapp.analytics.workmanger import com.rudderstack.android.sync.WorkManagerAnalyticsFactory import com.rudderstack.android.sync.WorkerManagerPlugin +import com.rudderstack.core.Analytics class SampleWorkManagerPlugin: WorkerManagerPlugin() { + + override lateinit var analytics: Analytics + override val workManagerAnalyticsFactoryClassName: Class get() = SampleWorkManagerAnalyticsFactory::class.java -} \ No newline at end of file +} diff --git a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt index 136db180..3e73ebf4 100644 --- a/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt +++ b/samples/sample-kotlin-android/src/main/kotlin/com/rudderstack/android/sampleapp/mainview/MainViewModel.kt @@ -2,9 +2,9 @@ package com.rudderstack.android.sampleapp.mainview import android.app.Application import androidx.lifecycle.AndroidViewModel -import com.rudderstack.android.utilities.applyConfigurationAndroid import com.rudderstack.android.sampleapp.analytics.RudderAnalyticsUtils import com.rudderstack.android.sampleapp.analytics.RudderAnalyticsUtils.analytics +import com.rudderstack.android.utilities.applyConfigurationAndroid import com.rudderstack.android.utilities.endSession import com.rudderstack.android.utilities.startSession import com.rudderstack.core.Analytics @@ -27,33 +27,29 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { private var _rudderReporter = RudderAnalyticsUtils.reporter - private val _loggingInterceptor by lazy { - object : Plugin { - private var _writeKey: String? = null - override fun setup(analytics: Analytics) { - _writeKey = analytics.writeKey - } - private var logsName: String = "Sample app-$_writeKey" + private val _loggingInterceptor = object : Plugin{ + override lateinit var analytics: Analytics - override fun intercept(chain: Plugin.Chain): Message { - val msg = chain.message() - _state.update { state -> - state.copy( - logDataList = state.logDataList + LogData( - Date(), "from $logsName, msg: $msg" - ) + override fun setup(analytics: Analytics) { + this.analytics = analytics + } + override fun intercept(chain: Plugin.Chain): Message { + val msg = chain.message() + _state.update { state -> + state.copy( + logDataList = state.logDataList + LogData( + Date(), "from ${"Sample app-${analytics.writeKey}"}, msg: $msg" ) - } - return chain.proceed(msg) + ) } - - + return chain.proceed(msg) } } init { analytics.addPlugin(_loggingInterceptor) } + private var extCount = 1 internal fun onEventClicked(analytics: AnalyticsState) { @@ -69,7 +65,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { trackProperties = TrackProperties("key1" to "prop1", "key2" to "prop2"), options = RudderOption().putIntegration("firebase", false) .putExternalId( - "fb_id","1234" + "fb_id", "1234" ) ) "Track message sent" @@ -81,7 +77,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { options = RudderOption().putExternalId("test_ext_id_key_$extCount", "test_val_$extCount") ) - ++ extCount + ++extCount "Identify called" } @@ -149,6 +145,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { RudderAnalyticsUtils.analytics.endSession() "Session Ended" } + AnalyticsState.SendError -> { _rudderReporter?.errorClient?.leaveBreadcrumb("Error BC") _rudderReporter?.errorClient?.addMetadata("Error MD", "md_key", "md_value")