mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-03 23:15:31 +00:00
[fenix] For https://github.com/mozilla-mobile/fenix/issues/27698: add set as default growth data
This commit is contained in:
parent
587b7920db
commit
088055743c
@ -1,4 +1,12 @@
|
|||||||
---
|
---
|
||||||
|
growth-data:
|
||||||
|
description: A feature measuring campaign growth data
|
||||||
|
hasExposure: true
|
||||||
|
exposureDescription: ""
|
||||||
|
variables:
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
|
description: "If true, the feature is active"
|
||||||
homescreen:
|
homescreen:
|
||||||
description: The homescreen that the user goes to when they press home or new tab.
|
description: The homescreen that the user goes to when they press home or new tab.
|
||||||
hasExposure: true
|
hasExposure: true
|
||||||
|
@ -377,6 +377,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
|
|||||||
if (settings().isMarketingTelemetryEnabled) {
|
if (settings().isMarketingTelemetryEnabled) {
|
||||||
components.analytics.metrics.start(MetricServiceType.Marketing)
|
components.analytics.metrics.start(MetricServiceType.Marketing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
components.appStore.dispatch(AppAction.MetricsInitializedAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun setupLeakCanary() {
|
protected open fun setupLeakCanary() {
|
||||||
|
@ -22,6 +22,7 @@ import org.mozilla.fenix.HomeActivity
|
|||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.ReleaseChannel
|
import org.mozilla.fenix.ReleaseChannel
|
||||||
import org.mozilla.fenix.components.metrics.AdjustMetricsService
|
import org.mozilla.fenix.components.metrics.AdjustMetricsService
|
||||||
|
import org.mozilla.fenix.components.metrics.DefaultMetricsStorage
|
||||||
import org.mozilla.fenix.components.metrics.GleanMetricsService
|
import org.mozilla.fenix.components.metrics.GleanMetricsService
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
import org.mozilla.fenix.experiments.createNimbus
|
import org.mozilla.fenix.experiments.createNimbus
|
||||||
@ -31,6 +32,7 @@ import org.mozilla.fenix.gleanplumb.NimbusMessagingStorage
|
|||||||
import org.mozilla.fenix.gleanplumb.OnDiskMessageMetadataStorage
|
import org.mozilla.fenix.gleanplumb.OnDiskMessageMetadataStorage
|
||||||
import org.mozilla.fenix.nimbus.FxNimbus
|
import org.mozilla.fenix.nimbus.FxNimbus
|
||||||
import org.mozilla.fenix.perf.lazyMonitored
|
import org.mozilla.fenix.perf.lazyMonitored
|
||||||
|
import org.mozilla.fenix.utils.BrowsersCache
|
||||||
import org.mozilla.geckoview.BuildConfig.MOZ_APP_BUILDID
|
import org.mozilla.geckoview.BuildConfig.MOZ_APP_BUILDID
|
||||||
import org.mozilla.geckoview.BuildConfig.MOZ_APP_VENDOR
|
import org.mozilla.geckoview.BuildConfig.MOZ_APP_VENDOR
|
||||||
import org.mozilla.geckoview.BuildConfig.MOZ_APP_VERSION
|
import org.mozilla.geckoview.BuildConfig.MOZ_APP_VERSION
|
||||||
@ -119,7 +121,15 @@ class Analytics(
|
|||||||
MetricController.create(
|
MetricController.create(
|
||||||
listOf(
|
listOf(
|
||||||
GleanMetricsService(context),
|
GleanMetricsService(context),
|
||||||
AdjustMetricsService(context as Application),
|
AdjustMetricsService(
|
||||||
|
application = context as Application,
|
||||||
|
storage = DefaultMetricsStorage(
|
||||||
|
context = context,
|
||||||
|
settings = context.settings(),
|
||||||
|
checkDefaultBrowser = { BrowsersCache.all(context).isDefaultBrowser },
|
||||||
|
),
|
||||||
|
crashReporter = crashReporter,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
isDataTelemetryEnabled = { context.settings().isTelemetryEnabled },
|
isDataTelemetryEnabled = { context.settings().isTelemetryEnabled },
|
||||||
isMarketingDataTelemetryEnabled = { context.settings().isMarketingTelemetryEnabled },
|
isMarketingDataTelemetryEnabled = { context.settings().isMarketingTelemetryEnabled },
|
||||||
|
@ -25,6 +25,7 @@ import org.mozilla.fenix.autofill.AutofillConfirmActivity
|
|||||||
import org.mozilla.fenix.autofill.AutofillSearchActivity
|
import org.mozilla.fenix.autofill.AutofillSearchActivity
|
||||||
import org.mozilla.fenix.autofill.AutofillUnlockActivity
|
import org.mozilla.fenix.autofill.AutofillUnlockActivity
|
||||||
import org.mozilla.fenix.components.appstate.AppState
|
import org.mozilla.fenix.components.appstate.AppState
|
||||||
|
import org.mozilla.fenix.components.metrics.MetricsMiddleware
|
||||||
import org.mozilla.fenix.datastore.pocketStoriesSelectedCategoriesDataStore
|
import org.mozilla.fenix.datastore.pocketStoriesSelectedCategoriesDataStore
|
||||||
import org.mozilla.fenix.ext.asRecentTabs
|
import org.mozilla.fenix.ext.asRecentTabs
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
@ -207,6 +208,7 @@ class Components(private val context: Context) {
|
|||||||
context.pocketStoriesSelectedCategoriesDataStore,
|
context.pocketStoriesSelectedCategoriesDataStore,
|
||||||
),
|
),
|
||||||
MessagingMiddleware(messagingStorage = analytics.messagingStorage),
|
MessagingMiddleware(messagingStorage = analytics.messagingStorage),
|
||||||
|
MetricsMiddleware(metrics = analytics.metrics),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -191,4 +191,9 @@ sealed class AppAction : Action {
|
|||||||
val imageState: Wallpaper.ImageFileState,
|
val imageState: Wallpaper.ImageFileState,
|
||||||
) : WallpaperAction()
|
) : WallpaperAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the app's metrics have been initialized and startup data can be sent.
|
||||||
|
*/
|
||||||
|
object MetricsInitializedAction : AppAction()
|
||||||
}
|
}
|
||||||
|
@ -220,6 +220,7 @@ internal object AppStoreReducer {
|
|||||||
val wallpaperState = state.wallpaperState.copy(availableWallpapers = wallpapers)
|
val wallpaperState = state.wallpaperState.copy(availableWallpapers = wallpapers)
|
||||||
state.copy(wallpaperState = wallpaperState)
|
state.copy(wallpaperState = wallpaperState)
|
||||||
}
|
}
|
||||||
|
is AppAction.MetricsInitializedAction -> state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,23 @@ import android.os.Bundle
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.adjust.sdk.Adjust
|
import com.adjust.sdk.Adjust
|
||||||
import com.adjust.sdk.AdjustConfig
|
import com.adjust.sdk.AdjustConfig
|
||||||
|
import com.adjust.sdk.AdjustEvent
|
||||||
import com.adjust.sdk.LogLevel
|
import com.adjust.sdk.LogLevel
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mozilla.components.lib.crash.CrashReporter
|
||||||
import org.mozilla.fenix.BuildConfig
|
import org.mozilla.fenix.BuildConfig
|
||||||
import org.mozilla.fenix.Config
|
import org.mozilla.fenix.Config
|
||||||
import org.mozilla.fenix.ext.settings
|
import org.mozilla.fenix.ext.settings
|
||||||
|
|
||||||
class AdjustMetricsService(private val application: Application) : MetricsService {
|
class AdjustMetricsService(
|
||||||
|
private val application: Application,
|
||||||
|
private val storage: MetricsStorage,
|
||||||
|
private val crashReporter: CrashReporter,
|
||||||
|
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||||
|
) : MetricsService {
|
||||||
override val type = MetricServiceType.Marketing
|
override val type = MetricServiceType.Marketing
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
@ -70,9 +81,22 @@ class AdjustMetricsService(private val application: Application) : MetricsServic
|
|||||||
Adjust.gdprForgetMe(application.applicationContext)
|
Adjust.gdprForgetMe(application.applicationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're not currently sending events directly to Adjust
|
@Suppress("TooGenericExceptionCaught")
|
||||||
override fun track(event: Event) { /* noop */ }
|
override fun track(event: Event) {
|
||||||
override fun shouldTrack(event: Event): Boolean = false
|
CoroutineScope(dispatcher).launch {
|
||||||
|
try {
|
||||||
|
if (event is Event.GrowthData && storage.shouldTrack(event)) {
|
||||||
|
Adjust.trackEvent(AdjustEvent(event.tokenName))
|
||||||
|
storage.updateSentState(event)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
crashReporter.submitCaughtException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldTrack(event: Event): Boolean =
|
||||||
|
event is Event.GrowthData
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val LOGTAG = "AdjustMetricsService"
|
private const val LOGTAG = "AdjustMetricsService"
|
||||||
|
@ -12,4 +12,14 @@ sealed class Event {
|
|||||||
|
|
||||||
internal open val extras: Map<*, String>?
|
internal open val extras: Map<*, String>?
|
||||||
get() = null
|
get() = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events related to growth campaigns.
|
||||||
|
*/
|
||||||
|
sealed class GrowthData(val tokenName: String) : Event() {
|
||||||
|
/**
|
||||||
|
* Event recording whether Firefox has been set as the default browser.
|
||||||
|
*/
|
||||||
|
object SetAsDefault : GrowthData("xgpcgt")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package org.mozilla.fenix.components.metrics
|
||||||
|
|
||||||
|
import mozilla.components.lib.state.Middleware
|
||||||
|
import mozilla.components.lib.state.MiddlewareContext
|
||||||
|
import org.mozilla.fenix.components.appstate.AppAction
|
||||||
|
import org.mozilla.fenix.components.appstate.AppState
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A middleware that will map incoming actions to relevant events for [metrics].
|
||||||
|
*/
|
||||||
|
class MetricsMiddleware(
|
||||||
|
private val metrics: MetricController,
|
||||||
|
) : Middleware<AppState, AppAction> {
|
||||||
|
override fun invoke(
|
||||||
|
context: MiddlewareContext<AppState, AppAction>,
|
||||||
|
next: (AppAction) -> Unit,
|
||||||
|
action: AppAction,
|
||||||
|
) {
|
||||||
|
handleAction(action)
|
||||||
|
next(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleAction(action: AppAction) = when (action) {
|
||||||
|
is AppAction.MetricsInitializedAction -> {
|
||||||
|
metrics.track(Event.GrowthData.SetAsDefault)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/* 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 android.content.Context
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.mozilla.fenix.ext.settings
|
||||||
|
import org.mozilla.fenix.nimbus.FxNimbus
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface defining functions around persisted local state for certain metrics.
|
||||||
|
*/
|
||||||
|
interface MetricsStorage {
|
||||||
|
/**
|
||||||
|
* Determines whether an [event] should be sent based on locally-stored state.
|
||||||
|
*/
|
||||||
|
suspend fun shouldTrack(event: Event): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates locally-stored state for an [event] that has just been sent.
|
||||||
|
*/
|
||||||
|
suspend fun updateSentState(event: Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultMetricsStorage(
|
||||||
|
context: Context,
|
||||||
|
private val settings: Settings,
|
||||||
|
private val checkDefaultBrowser: () -> Boolean,
|
||||||
|
private val shouldSendGenerally: () -> Boolean = { shouldSendGenerally(context) },
|
||||||
|
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||||
|
) : MetricsStorage {
|
||||||
|
/**
|
||||||
|
* Checks local state to see whether the [event] should be sent.
|
||||||
|
*/
|
||||||
|
override suspend fun shouldTrack(event: Event): Boolean =
|
||||||
|
withContext(dispatcher) {
|
||||||
|
shouldSendGenerally() && when (event) {
|
||||||
|
Event.GrowthData.SetAsDefault -> {
|
||||||
|
!settings.setAsDefaultGrowthSent && checkDefaultBrowser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateSentState(event: Event) = withContext(dispatcher) {
|
||||||
|
when (event) {
|
||||||
|
Event.GrowthData.SetAsDefault -> settings.setAsDefaultGrowthSent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
fun shouldSendGenerally(context: Context): Boolean {
|
||||||
|
val installedTime = context.packageManager
|
||||||
|
.getPackageInfo(context.packageName, 0)
|
||||||
|
.firstInstallTime
|
||||||
|
val timeDifference = System.currentTimeMillis() - installedTime
|
||||||
|
val withinWindow = timeDifference in windowStartMillis..windowEndMillis
|
||||||
|
|
||||||
|
return context.settings().adjustCampaignId.isNotEmpty() &&
|
||||||
|
FxNimbus.features.growthData.value().enabled &&
|
||||||
|
withinWindow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1413,4 +1413,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
|
|||||||
HttpsOnlyMode.ENABLED
|
HttpsOnlyMode.ENABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var setAsDefaultGrowthSent by booleanPreference(
|
||||||
|
key = appContext.getPreferenceKey(R.string.pref_key_growth_set_as_default),
|
||||||
|
default = false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -307,4 +307,7 @@
|
|||||||
<string name="pref_key_history_metadata_feature" translatable="false">pref_key_history_metadata_feature</string>
|
<string name="pref_key_history_metadata_feature" translatable="false">pref_key_history_metadata_feature</string>
|
||||||
<string name="pref_key_show_unified_search" translatable="false">pref_key_show_unified_search</string>
|
<string name="pref_key_show_unified_search" translatable="false">pref_key_show_unified_search</string>
|
||||||
<string name="pref_key_custom_glean_server_url" translatable="false">pref_key_custom_glean_server_url</string>
|
<string name="pref_key_custom_glean_server_url" translatable="false">pref_key_custom_glean_server_url</string>
|
||||||
|
|
||||||
|
<!-- Growth Data -->
|
||||||
|
<string name="pref_key_growth_set_as_default" translatable="false">pref_key_growth_set_as_default</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/* 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 io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.slot
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class DefaultMetricsStorageTest {
|
||||||
|
|
||||||
|
private var checkDefaultBrowser = false
|
||||||
|
private val doCheckDefaultBrowser = { checkDefaultBrowser }
|
||||||
|
private var shouldSendGenerally = true
|
||||||
|
private val doShouldSendGenerally = { shouldSendGenerally }
|
||||||
|
|
||||||
|
private val settings = mockk<Settings>()
|
||||||
|
|
||||||
|
private val dispatcher = StandardTestDispatcher()
|
||||||
|
|
||||||
|
private lateinit var storage: DefaultMetricsStorage
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
checkDefaultBrowser = false
|
||||||
|
shouldSendGenerally = true
|
||||||
|
storage = DefaultMetricsStorage(mockk(), settings, doCheckDefaultBrowser, doShouldSendGenerally, dispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN that events should not be generally sent WHEN event would be tracked THEN it is not`() = runTest(dispatcher) {
|
||||||
|
shouldSendGenerally = false
|
||||||
|
checkDefaultBrowser = true
|
||||||
|
every { settings.setAsDefaultGrowthSent } returns false
|
||||||
|
|
||||||
|
val result = storage.shouldTrack(Event.GrowthData.SetAsDefault)
|
||||||
|
|
||||||
|
assertFalse(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN set as default has not been sent and app is not default WHEN checked for sending THEN will not be sent`() = runTest(dispatcher) {
|
||||||
|
every { settings.setAsDefaultGrowthSent } returns false
|
||||||
|
checkDefaultBrowser = false
|
||||||
|
|
||||||
|
val result = storage.shouldTrack(Event.GrowthData.SetAsDefault)
|
||||||
|
|
||||||
|
assertFalse(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN set as default has not been sent and app is default WHEN checked for sending THEN will be sent`() = runTest(dispatcher) {
|
||||||
|
every { settings.setAsDefaultGrowthSent } returns false
|
||||||
|
checkDefaultBrowser = true
|
||||||
|
|
||||||
|
val result = storage.shouldTrack(Event.GrowthData.SetAsDefault)
|
||||||
|
|
||||||
|
assertTrue(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN set as default has been sent and app is default WHEN checked for sending THEN will be not sent`() = runTest(dispatcher) {
|
||||||
|
every { settings.setAsDefaultGrowthSent } returns true
|
||||||
|
checkDefaultBrowser = true
|
||||||
|
|
||||||
|
val result = storage.shouldTrack(Event.GrowthData.SetAsDefault)
|
||||||
|
|
||||||
|
assertFalse(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN set as default updated THEN settings will be updated accordingly`() = runTest(dispatcher) {
|
||||||
|
val updateSlot = slot<Boolean>()
|
||||||
|
every { settings.setAsDefaultGrowthSent = capture(updateSlot) } returns Unit
|
||||||
|
|
||||||
|
storage.updateSentState(Event.GrowthData.SetAsDefault)
|
||||||
|
|
||||||
|
assertTrue(updateSlot.captured)
|
||||||
|
}
|
||||||
|
}
|
@ -219,6 +219,18 @@ features:
|
|||||||
value:
|
value:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
growth-data:
|
||||||
|
description: A feature measuring campaign growth data
|
||||||
|
variables:
|
||||||
|
enabled:
|
||||||
|
description: If true, the feature is active
|
||||||
|
type: Boolean
|
||||||
|
default: false
|
||||||
|
defaults:
|
||||||
|
- channel: release
|
||||||
|
value:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
types:
|
types:
|
||||||
objects:
|
objects:
|
||||||
MessageData:
|
MessageData:
|
||||||
|
Loading…
Reference in New Issue
Block a user