diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index 77fedea9aa..9fec841404 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -23,14 +23,9 @@ sealed class Event { object SetAsDefault : GrowthData("xgpcgt") /** - * Event recording the first time Firefox has been resumed in a 24 hour period. + * Event recording that an ad was clicked in a search engine results page. */ - object FirstAppOpenForDay : GrowthData("41hl22") - - /** - * Event recording the first time a URI is loaded in Firefox in a 24 hour period. - */ - object FirstUriLoadForDay : GrowthData("ja86ek") + object SerpAdClicked : GrowthData("e2x17e") /** * Event recording the first time Firefox is used 3 days in a row in the first week of install. diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt index d5590efe71..6b1f949947 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt @@ -262,6 +262,7 @@ internal class ReleaseMetricController( Component.FEATURE_SEARCH to AdsTelemetry.SERP_ADD_CLICKED -> { BrowserSearch.adClicks[value!!].add() + track(Event.GrowthData.SerpAdClicked) } Component.FEATURE_SEARCH to AdsTelemetry.SERP_SHOWN_WITH_ADDS -> { BrowserSearch.withAds[value!!].add() diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsMiddleware.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsMiddleware.kt index e7d7cf2a17..83a1320f03 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsMiddleware.kt @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + package org.mozilla.fenix.components.metrics import mozilla.components.lib.state.Middleware @@ -23,7 +27,6 @@ class MetricsMiddleware( private fun handleAction(action: AppAction) = when (action) { is AppAction.ResumedMetricsAction -> { metrics.track(Event.GrowthData.SetAsDefault) - metrics.track(Event.GrowthData.FirstAppOpenForDay) metrics.track(Event.GrowthData.FirstWeekSeriesActivity) } else -> Unit diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt index f19557f7bf..7cb678a8a4 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricsStorage.kt @@ -47,21 +47,20 @@ internal class DefaultMetricsStorage( */ override suspend fun shouldTrack(event: Event): Boolean = withContext(dispatcher) { - // The side-effect of storing days of use needs to happen during the first two days after - // install, which would normally be skipped by shouldSendGenerally. + // The side-effect of storing days of use always needs to happen. updateDaysOfUse() + val currentTime = System.currentTimeMillis() shouldSendGenerally() && when (event) { Event.GrowthData.SetAsDefault -> { - !settings.setAsDefaultGrowthSent && checkDefaultBrowser() - } - Event.GrowthData.FirstAppOpenForDay -> { - settings.resumeGrowthLastSent.hasBeenMoreThanDaySince() - } - Event.GrowthData.FirstUriLoadForDay -> { - settings.uriLoadGrowthLastSent.hasBeenMoreThanDaySince() + currentTime.duringFirstMonth() && + !settings.setAsDefaultGrowthSent && + checkDefaultBrowser() } Event.GrowthData.FirstWeekSeriesActivity -> { - shouldTrackFirstWeekActivity() + currentTime.duringFirstMonth() && shouldTrackFirstWeekActivity() + } + Event.GrowthData.SerpAdClicked -> { + currentTime.duringFirstMonth() && !settings.adClickGrowthSent } } } @@ -71,15 +70,12 @@ internal class DefaultMetricsStorage( Event.GrowthData.SetAsDefault -> { settings.setAsDefaultGrowthSent = true } - Event.GrowthData.FirstAppOpenForDay -> { - settings.resumeGrowthLastSent = System.currentTimeMillis() - } - Event.GrowthData.FirstUriLoadForDay -> { - settings.uriLoadGrowthLastSent = System.currentTimeMillis() - } Event.GrowthData.FirstWeekSeriesActivity -> { settings.firstWeekSeriesGrowthSent = true } + Event.GrowthData.SerpAdClicked -> { + settings.adClickGrowthSent = true + } } } @@ -87,13 +83,13 @@ internal class DefaultMetricsStorage( val daysOfUse = settings.firstWeekDaysOfUseGrowthData val currentDate = Calendar.getInstance(Locale.US) val currentDateString = dateFormatter.format(currentDate.time) - if (currentDate.timeInMillis.withinFirstWeek() && daysOfUse.none { it == currentDateString }) { + if (currentDate.timeInMillis.duringFirstWeek() && daysOfUse.none { it == currentDateString }) { settings.firstWeekDaysOfUseGrowthData = daysOfUse + currentDateString } } private fun shouldTrackFirstWeekActivity(): Boolean = Result.runCatching { - if (!System.currentTimeMillis().withinFirstWeek() || settings.firstWeekSeriesGrowthSent) { + if (!System.currentTimeMillis().duringFirstWeek() || settings.firstWeekSeriesGrowthSent) { return false } @@ -121,14 +117,13 @@ internal class DefaultMetricsStorage( return false }.getOrDefault(false) - private fun Long.hasBeenMoreThanDaySince(): Boolean = - System.currentTimeMillis() - this > dayMillis - private fun Long.toCalendar(): Calendar = Calendar.getInstance(Locale.US).also { calendar -> calendar.timeInMillis = this } - private fun Long.withinFirstWeek() = this < getInstalledTime() + fullWeekMillis + private fun Long.duringFirstWeek() = this < getInstalledTime() + fullWeekMillis + + private fun Long.duringFirstMonth() = this < getInstalledTime() + shortestMonthMillis private fun Calendar.createNextDay() = (this.clone() as Calendar).also { calendar -> calendar.add(Calendar.DAY_OF_MONTH, 1) @@ -136,8 +131,7 @@ internal class DefaultMetricsStorage( companion object { private const val dayMillis: Long = 1000 * 60 * 60 * 24 - private const val windowStartMillis: Long = dayMillis * 2 - private const val windowEndMillis: Long = dayMillis * 28 + private const val shortestMonthMillis: Long = dayMillis * 28 // Note this is 8 so that recording of FirstWeekSeriesActivity happens throughout the length // of the 7th day after install @@ -146,17 +140,11 @@ internal class DefaultMetricsStorage( /** * Determines whether events should be tracked based on some general criteria: * - user has installed as a result of a campaign - * - user is within 2-28 days of install * - tracking is still enabled through Nimbus */ fun shouldSendGenerally(context: Context): Boolean { - val installedTime = getInstalledTime(context) - val timeDifference = System.currentTimeMillis() - installedTime - val withinWindow = timeDifference in windowStartMillis..windowEndMillis - return context.settings().adjustCampaignId.isNotEmpty() && - FxNimbus.features.growthData.value().enabled && - withinWindow + FxNimbus.features.growthData.value().enabled } fun getInstalledTime(context: Context): Long = context.packageManager diff --git a/app/src/main/java/org/mozilla/fenix/telemetry/TelemetryMiddleware.kt b/app/src/main/java/org/mozilla/fenix/telemetry/TelemetryMiddleware.kt index c53ca405fe..3c138f6d40 100644 --- a/app/src/main/java/org/mozilla/fenix/telemetry/TelemetryMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/telemetry/TelemetryMiddleware.kt @@ -21,7 +21,6 @@ import mozilla.components.support.base.android.Clock import mozilla.components.support.base.log.logger.Logger import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Metrics -import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.GleanMetrics.EngineTab as EngineMetrics @@ -60,9 +59,6 @@ class TelemetryMiddleware( val tab = context.state.findTabOrCustomTab(action.tabId) onEngineSessionKilled(context.state, tab) } - is EngineAction.LoadUrlAction -> { - metrics.track(Event.GrowthData.FirstUriLoadForDay) - } else -> { // no-op } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 60e03255ba..b13d8b5f0f 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -1503,16 +1503,6 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = false, ) - var resumeGrowthLastSent by longPreference( - key = appContext.getPreferenceKey(R.string.pref_key_growth_resume_last_sent), - default = 0, - ) - - var uriLoadGrowthLastSent by longPreference( - key = appContext.getPreferenceKey(R.string.pref_key_growth_uri_load_last_sent), - default = 0, - ) - var firstWeekSeriesGrowthSent by booleanPreference( key = appContext.getPreferenceKey(R.string.pref_key_growth_first_week_series_sent), default = false, @@ -1522,4 +1512,9 @@ class Settings(private val appContext: Context) : PreferencesHolder { key = appContext.getPreferenceKey(R.string.pref_key_growth_first_week_days_of_use), default = setOf(), ) + + var adClickGrowthSent by booleanPreference( + key = appContext.getPreferenceKey(R.string.pref_key_growth_ad_click_sent), + default = false, + ) } diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index f91762d0ec..015565f0bd 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -318,8 +318,7 @@ pref_key_growth_set_as_default - pref_key_growth_last_resumed - pref_key_growth_uri_load_last_sent pref_key_growth_first_week_series_sent pref_key_growth_first_week_days_of_use + pref_key_growth_ad_click_sent diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/DefaultMetricsStorageTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/DefaultMetricsStorageTest.kt index 4266a4deee..d3f5d34819 100644 --- a/app/src/test/java/org/mozilla/fenix/components/metrics/DefaultMetricsStorageTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/metrics/DefaultMetricsStorageTest.kt @@ -100,68 +100,6 @@ class DefaultMetricsStorageTest { assertTrue(updateSlot.captured) } - @Test - fun `GIVEN that it has been less than 24 hours since last resumed sent WHEN checked for sending THEN will not be sent`() = runTest(dispatcher) { - val currentTime = System.currentTimeMillis() - every { settings.resumeGrowthLastSent } returns currentTime - - val result = storage.shouldTrack(Event.GrowthData.FirstAppOpenForDay) - - assertFalse(result) - } - - @Test - fun `GIVEN that it has been more than 24 hours since last resumed sent WHEN checked for sending THEN will be sent`() = runTest(dispatcher) { - val currentTime = System.currentTimeMillis() - every { settings.resumeGrowthLastSent } returns currentTime - 1000 * 60 * 60 * 24 * 2 - - val result = storage.shouldTrack(Event.GrowthData.FirstAppOpenForDay) - - assertTrue(result) - } - - @Test - fun `WHEN last resumed state updated THEN settings updated accordingly`() = runTest(dispatcher) { - val updateSlot = slot() - every { settings.resumeGrowthLastSent } returns 0 - every { settings.resumeGrowthLastSent = capture(updateSlot) } returns Unit - - storage.updateSentState(Event.GrowthData.FirstAppOpenForDay) - - assertTrue(updateSlot.captured > 0) - } - - @Test - fun `GIVEN that it has been less than 24 hours since uri load sent WHEN checked for sending THEN will not be sent`() = runTest(dispatcher) { - val currentTime = System.currentTimeMillis() - every { settings.uriLoadGrowthLastSent } returns currentTime - - val result = storage.shouldTrack(Event.GrowthData.FirstUriLoadForDay) - - assertFalse(result) - } - - @Test - fun `GIVEN that it has been more than 24 hours since uri load sent WHEN checked for sending THEN will be sent`() = runTest(dispatcher) { - val currentTime = System.currentTimeMillis() - every { settings.uriLoadGrowthLastSent } returns currentTime - 1000 * 60 * 60 * 24 * 2 - - val result = storage.shouldTrack(Event.GrowthData.FirstUriLoadForDay) - - assertTrue(result) - } - - @Test - fun `WHEN uri load updated THEN settings updated accordingly`() = runTest(dispatcher) { - val updateSlot = slot() - every { settings.uriLoadGrowthLastSent } returns 0 - every { settings.uriLoadGrowthLastSent = capture(updateSlot) } returns Unit - - storage.updateSentState(Event.GrowthData.FirstUriLoadForDay) - - assertTrue(updateSlot.captured > 0) - } - @Test fun `GIVEN that app has been used for less than 3 days in a row WHEN checked for first week activity THEN event will not be sent`() = runTest(dispatcher) { val tomorrow = calendarStart.createNextDay() @@ -273,6 +211,24 @@ class DefaultMetricsStorageTest { assertFalse(captureSlot.isCaptured) } + @Test + fun `GIVEN serp ad clicked event already sent WHEN checking to track serp ad clicked THEN event will not be sent`() = runTest(dispatcher) { + every { settings.adClickGrowthSent } returns true + + val result = storage.shouldTrack(Event.GrowthData.SerpAdClicked) + + assertFalse(result) + } + + @Test + fun `GIVEN serp ad clicked event not sent WHEN checking to track serp ad clicked THEN event will be sent`() = runTest(dispatcher) { + every { settings.adClickGrowthSent } returns false + + val result = storage.shouldTrack(Event.GrowthData.SerpAdClicked) + + assertTrue(result) + } + private fun Calendar.copy() = clone() as Calendar private fun Calendar.createNextDay() = copy().apply { add(Calendar.DAY_OF_MONTH, 1) diff --git a/app/src/test/java/org/mozilla/fenix/telemetry/TelemetryMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/telemetry/TelemetryMiddlewareTest.kt index 77cf0b3832..b8bd91bf92 100644 --- a/app/src/test/java/org/mozilla/fenix/telemetry/TelemetryMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/telemetry/TelemetryMiddlewareTest.kt @@ -6,7 +6,6 @@ package org.mozilla.fenix.telemetry import androidx.test.core.app.ApplicationProvider import io.mockk.mockk -import io.mockk.verify import mozilla.components.browser.state.action.ContentAction import mozilla.components.browser.state.action.EngineAction import mozilla.components.browser.state.action.TabListAction @@ -33,7 +32,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Metrics -import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.utils.Settings @@ -352,13 +350,6 @@ class TelemetryMiddlewareTest { assertNull(EngineMetrics.killForegroundAge.testGetValue()) assertEquals(600_000_000, EngineMetrics.killBackgroundAge.testGetValue()!!.sum) } - - @Test - fun `WHEN uri loaded to engine THEN matching event is sent to metrics`() { - store.dispatch(EngineAction.LoadUrlAction("", "")).joinBlocking() - - verify { metrics.track(Event.GrowthData.FirstUriLoadForDay) } - } } internal class FakeClock : Clock.Delegate {