Skip to content

Commit

Permalink
Measure impact of events in retention (#3224)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1204601774518723/f

### Description
Implemented new pixel which will send as parameters app events performed prior to the specified date. 

### Steps to test this PR

- smoke test

Asana task: https://app.asana.com/0/0/1204601774518724/f

### No UI changes
  • Loading branch information
nalcalag committed Jun 2, 2023
1 parent 6d64f9e commit 7ca2c3b
Show file tree
Hide file tree
Showing 35 changed files with 477 additions and 17 deletions.
Expand Up @@ -19,6 +19,8 @@ package com.duckduckgo.mobile.android.vpn
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.mobile.android.vpn.prefs.VpnSharedPreferencesProvider
import com.duckduckgo.mobile.android.vpn.service.TrackerBlockingVpnService
import java.util.UUID
Expand All @@ -30,6 +32,7 @@ private const val IS_INITIALIZED = "IS_INITIALIZED"
internal class VpnFeaturesRegistryImpl(
private val vpnServiceWrapper: VpnServiceWrapper,
private val sharedPreferencesProvider: VpnSharedPreferencesProvider,
private val featureSegmentsManager: FeatureSegmentsManager,
) : VpnFeaturesRegistry {

private val preferences: SharedPreferences by lazy {
Expand All @@ -44,6 +47,7 @@ internal class VpnFeaturesRegistryImpl(
putString(feature.featureName, UUID.randomUUID().toString())
}
vpnServiceWrapper.restartVpnService(forceRestart = true)
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.APP_TP_ENABLED)
}

@Synchronized
Expand Down
Expand Up @@ -21,6 +21,7 @@ import android.content.res.Resources
import android.net.ConnectivityManager
import androidx.room.Room
import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistryImpl
Expand Down Expand Up @@ -99,8 +100,9 @@ object VpnAppModule {
fun provideVpnFeaturesRegistry(
context: Context,
sharedPreferencesProvider: VpnSharedPreferencesProvider,
featureSegmentsManager: FeatureSegmentsManager,
): VpnFeaturesRegistry {
return VpnFeaturesRegistryImpl(VpnServiceWrapper(context), sharedPreferencesProvider)
return VpnFeaturesRegistryImpl(VpnServiceWrapper(context), sharedPreferencesProvider, featureSegmentsManager)
}

@Provides
Expand Down
Expand Up @@ -19,6 +19,7 @@ package com.duckduckgo.mobile.android.vpn
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.duckduckgo.app.global.api.InMemorySharedPreferences
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.mobile.android.vpn.prefs.VpnSharedPreferencesProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
Expand All @@ -33,6 +34,7 @@ import org.mockito.kotlin.*
class VpnFeaturesRegistryImplTest {

private val sharedPreferencesProvider: VpnSharedPreferencesProvider = mock()
private val mockFeatureSegmentsManager: FeatureSegmentsManager = mock()
private lateinit var vpnServiceWrapper: TestVpnServiceWrapper

private lateinit var vpnFeaturesRegistry: VpnFeaturesRegistry
Expand All @@ -46,7 +48,7 @@ class VpnFeaturesRegistryImplTest {
sharedPreferencesProvider.getSharedPreferences(eq("com.duckduckgo.mobile.android.vpn.feature.registry.v1"), eq(true), eq(false)),
).thenReturn(prefs)

vpnFeaturesRegistry = VpnFeaturesRegistryImpl(vpnServiceWrapper, sharedPreferencesProvider)
vpnFeaturesRegistry = VpnFeaturesRegistryImpl(vpnServiceWrapper, sharedPreferencesProvider, mockFeatureSegmentsManager)
}

@Test
Expand Down
Expand Up @@ -108,6 +108,7 @@ import com.duckduckgo.app.privacy.model.UserWhitelistedDomain
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.api.StatisticsUpdater
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter.CTA_SHOWN
import com.duckduckgo.app.statistics.pixels.Pixel.PixelValues.DAX_APPTP_CTA
Expand Down Expand Up @@ -333,6 +334,9 @@ class BrowserTabViewModelTest {
@Mock
private lateinit var mockUserAllowListRepository: UserAllowListRepository

@Mock
private lateinit var mockFeatureSegmentsManager: FeatureSegmentsManager

private lateinit var remoteMessagingModel: RemoteMessagingModel

private val lazyFaviconManager = Lazy { mockFaviconManager }
Expand Down Expand Up @@ -504,6 +508,7 @@ class BrowserTabViewModelTest {
autofillCapabilityChecker = autofillCapabilityChecker,
autofillFireproofDialogSuppressor = autofillFireproofDialogSuppressor,
automaticSavedLoginsMonitor = automaticSavedLoginsMonitor,
featureSegmentsManager = mockFeatureSegmentsManager,
)

testee.loadData("abc", null, false, false)
Expand Down
Expand Up @@ -25,6 +25,7 @@ import com.duckduckgo.app.statistics.AtbInitializerListener
import com.duckduckgo.app.statistics.api.PixelSender
import com.duckduckgo.app.statistics.api.StatisticsService
import com.duckduckgo.app.statistics.api.StatisticsUpdater
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import com.duckduckgo.di.DaggerSet
Expand Down Expand Up @@ -109,8 +110,9 @@ class StubStatisticsModule {
statisticsDataStore: StatisticsDataStore,
statisticsUpdater: StatisticsUpdater,
listeners: DaggerSet<AtbInitializerListener>,
featureSegmentsManager: FeatureSegmentsManager,
): MainProcessLifecycleObserver {
return AtbInitializer(appCoroutineScope, statisticsDataStore, statisticsUpdater, listeners)
return AtbInitializer(appCoroutineScope, statisticsDataStore, statisticsUpdater, listeners, featureSegmentsManager)
}

@Provides
Expand Down
Expand Up @@ -27,6 +27,7 @@ import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.api.RefreshRetentionAtbPlugin
import com.duckduckgo.app.statistics.api.StatisticsRequester
import com.duckduckgo.app.statistics.api.StatisticsService
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.model.Atb
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import com.duckduckgo.app.statistics.store.StatisticsSharedPreferences
Expand Down
Expand Up @@ -27,6 +27,8 @@ import com.duckduckgo.app.browser.favicon.FaviconManager
import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.app.global.SingleLiveEvent
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType.BOOKMARKS_IMPORTED
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.savedsites.api.SavedSitesRepository
Expand Down Expand Up @@ -55,6 +57,7 @@ class BookmarksViewModel @Inject constructor(
private val pixel: Pixel,
private val syncEngine: SyncEngine,
private val dispatcherProvider: DispatcherProvider,
private val featureSegmentsManager: FeatureSegmentsManager,
) : EditSavedSiteListener, AddBookmarkFolderListener, EditBookmarkFolderListener, ViewModel() {

data class ViewState(
Expand Down Expand Up @@ -159,6 +162,9 @@ class BookmarksViewModel @Inject constructor(
withContext(dispatcherProvider.main()) {
command.value = ImportedSavedSites(result)
}
if (result is ImportSavedSitesResult.Success) {
featureSegmentsManager.addUserToFeatureSegment(BOOKMARKS_IMPORTED)
}
}
}

Expand Down
Expand Up @@ -63,6 +63,7 @@ import com.duckduckgo.app.settings.SettingsActivity
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.sitepermissions.SitePermissionsActivity
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.tabs.model.TabEntity
import com.duckduckgo.di.scopes.ActivityScope
Expand Down Expand Up @@ -115,6 +116,9 @@ open class BrowserActivity : DuckDuckGoActivity() {
@Inject
lateinit var globalActivityStarter: GlobalActivityStarter

@Inject
lateinit var featureSegmentsManager: FeatureSegmentsManager

@Inject
@AppCoroutineScope
lateinit var appCoroutineScope: CoroutineScope
Expand Down Expand Up @@ -423,6 +427,7 @@ open class BrowserActivity : DuckDuckGoActivity() {
settingsDataStore = settingsDataStore,
userEventsStore = userEventsStore,
appCoroutineScope = appCoroutineScope,
featureSegmentsManager = featureSegmentsManager,
)
dialog.clearStarted = {
removeObservers()
Expand Down
Expand Up @@ -99,6 +99,8 @@ import com.duckduckgo.app.privacy.db.NetworkLeaderboardDao
import com.duckduckgo.app.privacy.db.UserWhitelistDao
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.api.StatisticsUpdater
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter.FAVORITE_MENU_ITEM_STATE
Expand Down Expand Up @@ -189,6 +191,7 @@ class BrowserTabViewModel @Inject constructor(
private val sitePermissionsManager: SitePermissionsManager,
private val autofillFireproofDialogSuppressor: AutofillFireproofDialogSuppressor,
private val automaticSavedLoginsMonitor: AutomaticSavedLoginsMonitor,
private val featureSegmentsManager: FeatureSegmentsManager,
) : WebViewClientListener,
EditSavedSiteListener,
UrlExtractionListener,
Expand Down Expand Up @@ -880,6 +883,7 @@ class BrowserTabViewModel @Inject constructor(
viewModelScope.launch(dispatchers.io()) {
searchCountDao.incrementSearchCount()
}
featureSegmentsManager.searchMade()

val verticalParameter = extractVerticalParameter(url)
var urlToNavigate = queryUrlConverter.convertQueryToUrl(trimmedInput, verticalParameter, queryOrigin)
Expand Down Expand Up @@ -1897,6 +1901,7 @@ class BrowserTabViewModel @Inject constructor(
viewModelScope.launch {
val favorite = withContext(dispatchers.io()) {
if (url.isNotBlank()) {
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.FAVOURITE_SET)
faviconManager.persistCachedFavicon(tabId, url)
savedSitesRepository.insertFavorite(title = title, url = url)
} else {
Expand Down Expand Up @@ -2793,6 +2798,7 @@ class BrowserTabViewModel @Inject constructor(
url: String,
credentials: LoginCredentials,
): LoginCredentials? {
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.LOGIN_SAVED)
return withContext(appCoroutineScope.coroutineContext) {
autofillStore.saveCredentials(url, credentials)
}
Expand Down
Expand Up @@ -25,6 +25,8 @@ import com.duckduckgo.app.browser.R
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.global.DefaultDispatcherProvider
import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.autofill.api.CredentialAutofillPickerDialog
import com.duckduckgo.autofill.api.CredentialSavePickerDialog
Expand Down Expand Up @@ -64,6 +66,7 @@ class AutofillCredentialsSelectionResultHandler @Inject constructor(
private val autofillDialogSuppressor: AutofillFireproofDialogSuppressor,
private val autoSavedLoginsMonitor: AutomaticSavedLoginsMonitor,
private val existingCredentialMatchDetector: ExistingCredentialMatchDetector,
private val featureSegmentsManager: FeatureSegmentsManager,
) {

suspend fun processAutofillCredentialSelectionResult(
Expand Down Expand Up @@ -244,6 +247,7 @@ class AutofillCredentialsSelectionResultHandler @Inject constructor(
)?.id?.let { savedId ->
Timber.i("New login saved because no exact matches were found, with ID: $savedId")
autoSavedLoginsMonitor.setAutoSavedLoginId(savedId, tabId)
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.LOGIN_SAVED)
}
}
}
Expand Down
Expand Up @@ -20,13 +20,16 @@ import androidx.lifecycle.LifecycleOwner
import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter

class DefaultBrowserObserver(
private val defaultBrowserDetector: DefaultBrowserDetector,
private val appInstallStore: AppInstallStore,
private val pixel: Pixel,
private val featureSegmentsManager: FeatureSegmentsManager,
) : MainProcessLifecycleObserver {

override fun onResume(owner: LifecycleOwner) {
Expand All @@ -39,6 +42,7 @@ class DefaultBrowserObserver(
PixelParameter.DEFAULT_BROWSER_SET_FROM_ONBOARDING to false.toString(),
)
pixel.fire(AppPixelName.DEFAULT_BROWSER_SET, params)
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.SET_AS_DEFAULT)
}
else -> pixel.fire(AppPixelName.DEFAULT_BROWSER_UNSET)
}
Expand Down
Expand Up @@ -62,6 +62,7 @@ import com.duckduckgo.app.privacy.db.UserAllowListRepository
import com.duckduckgo.app.referral.AppReferrerDataStore
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import com.duckduckgo.app.surrogates.ResourceSurrogates
Expand Down Expand Up @@ -144,8 +145,9 @@ class BrowserModule {
defaultBrowserDetector: DefaultBrowserDetector,
appInstallStore: AppInstallStore,
pixel: Pixel,
featureSegmentsManager: FeatureSegmentsManager,
): MainProcessLifecycleObserver {
return DefaultBrowserObserver(defaultBrowserDetector, appInstallStore, pixel)
return DefaultBrowserObserver(defaultBrowserDetector, appInstallStore, pixel, featureSegmentsManager)
}

@SingleInstanceIn(AppScope::class)
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/duckduckgo/app/di/StatisticsModule.kt
Expand Up @@ -24,6 +24,7 @@ import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
import com.duckduckgo.app.statistics.AtbInitializer
import com.duckduckgo.app.statistics.AtbInitializerListener
import com.duckduckgo.app.statistics.api.*
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.store.PendingPixelDao
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import com.duckduckgo.di.DaggerSet
Expand Down Expand Up @@ -55,8 +56,9 @@ object StatisticsModule {
statisticsDataStore: StatisticsDataStore,
statisticsUpdater: StatisticsUpdater,
listeners: DaggerSet<AtbInitializerListener>,
featureSegmentsManager: FeatureSegmentsManager,
): MainProcessLifecycleObserver {
return AtbInitializer(appCoroutineScope, statisticsDataStore, statisticsUpdater, listeners)
return AtbInitializer(appCoroutineScope, statisticsDataStore, statisticsUpdater, listeners, featureSegmentsManager)
}

@SingleInstanceIn(AppScope::class)
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/duckduckgo/app/email/AppEmailManager.kt
Expand Up @@ -22,6 +22,8 @@ import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.app.pixels.AppPixelName.EMAIL_DISABLED
import com.duckduckgo.app.pixels.AppPixelName.EMAIL_ENABLED
import com.duckduckgo.app.statistics.api.BrowserFeatureStateReporterPlugin
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType.EMAIL_PROTECTION_SET
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter
import com.duckduckgo.di.scopes.AppScope
Expand All @@ -48,6 +50,7 @@ class AppEmailManager @Inject constructor(
private val dispatcherProvider: DispatcherProvider,
private val appCoroutineScope: CoroutineScope,
private val pixel: Pixel,
private val featureSegmentsManager: FeatureSegmentsManager,
) : EmailManager, BrowserFeatureStateReporterPlugin {

private val isSignedInStateFlow = MutableStateFlow(isSignedIn())
Expand All @@ -72,6 +75,7 @@ class AppEmailManager @Inject constructor(
generateNewAlias()
pixel.fire(EMAIL_ENABLED)
}
featureSegmentsManager.addUserToFeatureSegment(EMAIL_PROTECTION_SET)
}

override fun signOut() {
Expand Down
Expand Up @@ -40,6 +40,8 @@ import com.duckduckgo.app.global.view.FireDialog.FireDialogClearAllEvent.ClearAl
import com.duckduckgo.app.pixels.AppPixelName.*
import com.duckduckgo.app.settings.clear.getPixelValue
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentType
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter.FIRE_ANIMATION
import com.duckduckgo.mobile.android.R as CommonR
Expand All @@ -63,6 +65,7 @@ class FireDialog(
private val settingsDataStore: SettingsDataStore,
private val userEventsStore: UserEventsStore,
private val appCoroutineScope: CoroutineScope,
private val featureSegmentsManager: FeatureSegmentsManager,
) : BottomSheetDialog(context, com.duckduckgo.mobile.android.R.style.Widget_DuckDuckGo_FireDialog) {

private lateinit var binding: SheetFireClearDataBinding
Expand Down Expand Up @@ -199,6 +202,7 @@ class FireDialog(
canRestart = true
if (event is ClearAllDataFinished) {
binding.fireAnimationView.addAnimatorUpdateListener(accelerateAnimatorUpdateListener)
featureSegmentsManager.addUserToFeatureSegment(FeatureSegmentType.FIRE_BUTTON_USED)
}
} else {
clearPersonalDataAction.killAndRestartProcess(notifyDataCleared = false)
Expand Down
Expand Up @@ -21,6 +21,7 @@ import com.duckduckgo.app.global.DefaultRoleBrowserDialog
import com.duckduckgo.app.global.RealDefaultRoleBrowserDialog
import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModelFactory
import com.duckduckgo.app.statistics.api.featureusage.FeatureSegmentsManager
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import dagger.Module
Expand All @@ -35,7 +36,8 @@ class WelcomePageModule {
context: Context,
pixel: Pixel,
defaultRoleBrowserDialog: DefaultRoleBrowserDialog,
) = WelcomePageViewModelFactory(appInstallStore, context, pixel, defaultRoleBrowserDialog)
featureSegmentsManager: FeatureSegmentsManager,
) = WelcomePageViewModelFactory(appInstallStore, context, pixel, defaultRoleBrowserDialog, featureSegmentsManager)

@Provides
fun defaultRoleBrowserDialog(
Expand Down

0 comments on commit 7ca2c3b

Please sign in to comment.