From 23603e6274b28512af76889f4d0d977189e19c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matu=CC=81s=CC=8C=20Tomlein?= Date: Fri, 8 Dec 2023 17:00:09 +0100 Subject: [PATCH] Do not track the screen view again when app comes to foreground (close #653) --- .../screen/ScreenViewAutotrackingTest.kt | 90 +++++++++++++++++++ .../core/tracker/ScreenState.kt | 4 +- .../snowplowanalytics/core/tracker/Tracker.kt | 17 +++- 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/screen/ScreenViewAutotrackingTest.kt diff --git a/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/screen/ScreenViewAutotrackingTest.kt b/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/screen/ScreenViewAutotrackingTest.kt new file mode 100644 index 000000000..f94ce985e --- /dev/null +++ b/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/screen/ScreenViewAutotrackingTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015-2023 Snowplow Analytics Ltd. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package com.snowplowanalytics.snowplow.tracker + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.snowplowanalytics.core.constants.TrackerConstants +import com.snowplowanalytics.core.utils.NotificationCenter +import com.snowplowanalytics.snowplow.Snowplow +import com.snowplowanalytics.snowplow.Snowplow.removeAllTrackers +import com.snowplowanalytics.snowplow.configuration.Configuration +import com.snowplowanalytics.snowplow.configuration.NetworkConfiguration +import com.snowplowanalytics.snowplow.configuration.PluginConfiguration +import com.snowplowanalytics.snowplow.controller.TrackerController +import com.snowplowanalytics.snowplow.event.ScreenView +import com.snowplowanalytics.snowplow.event.Structured +import com.snowplowanalytics.snowplow.network.HttpMethod +import com.snowplowanalytics.snowplow.payload.SelfDescribingJson +import org.junit.After +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* + +@RunWith(AndroidJUnit4::class) +class ScreenViewAutotrackingTest { + + @After + fun tearDown() { + removeAllTrackers() + } + + // --- TESTS + @Test + fun doesntTrackTheSameScreenViewMultipleTimes() { + var numberOfScreenViews = 0 + + val countPlugin = PluginConfiguration("test") + countPlugin.afterTrack { + if (it.schema == TrackerConstants.SCHEMA_SCREEN_VIEW) { + numberOfScreenViews += 1 + } + } + + createTracker(listOf(countPlugin)) + Thread.sleep(200) + + NotificationCenter.postNotification("SnowplowScreenView", mapOf( + "event" to ScreenView(name = "Screen1").activityClassName("Screen1") + )) + Thread.sleep(200) + + NotificationCenter.postNotification("SnowplowScreenView", mapOf( + "event" to ScreenView(name = "Screen1").activityClassName("Screen1") + )) + Thread.sleep(200) + + NotificationCenter.postNotification("SnowplowScreenView", mapOf( + "event" to ScreenView(name = "Screen2").activityClassName("Screen2") + )) + Thread.sleep(200) + + Assert.assertEquals(2, numberOfScreenViews) + } + + // --- PRIVATE + private val context: Context + get() = InstrumentationRegistry.getInstrumentation().targetContext + + private fun createTracker(configurations: List): TrackerController { + val networkConfig = NetworkConfiguration(MockNetworkConnection(HttpMethod.POST, 200)) + return Snowplow.createTracker( + context, + namespace = "testScreenView" + Math.random().toString(), + network = networkConfig, + configurations = configurations.toTypedArray() + ) + } +} diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/ScreenState.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/ScreenState.kt index 09071c515..29ca7e291 100644 --- a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/ScreenState.kt +++ b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/ScreenState.kt @@ -34,8 +34,8 @@ class ScreenState : State { private var transitionType: String? = null private var fragmentClassName: String? = null private var fragmentTag: String? = null - private var activityClassName: String? = null - private var activityTag: String? = null + var activityClassName: String? = null + var activityTag: String? = null init { id = uUIDString() diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/Tracker.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/Tracker.kt index ecfc8b691..5a0d47d15 100755 --- a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/Tracker.kt +++ b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/Tracker.kt @@ -65,7 +65,7 @@ class Tracker( private val stateManager = StateManager() fun getScreenState(): ScreenState? { - val state = stateManager.trackerState.getState("ScreenContext") + val state = stateManager.trackerState.getState(ScreenStateMachine.ID) ?: // Legacy initialization return ScreenState() @@ -324,8 +324,19 @@ class Tracker( private val receiveScreenViewNotification: FunctionalObserver = object : FunctionalObserver() { override fun apply(data: Map) { if (screenViewAutotracking) { - val event = data["event"] as? Event? - event?.let { track(it) } + val event = data["event"] as? ScreenView? + event?.let { event -> + getScreenState()?.let { state -> + // don't track if screen view is for the same activity as the last one + if ( + event.activityClassName?.isEmpty() != false || + event.activityClassName != state.activityClassName || + event.activityTag != state.activityTag + ) { + track(event) + } + } ?: track(event) + } } } }