Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,6 @@ class BrowserTabViewModelTest {
subscriptions = subscriptions,
sslCertificatesFeature = mockSSLCertificatesFeature,
bypassedSSLCertificatesRepository = mockBypassedSSLCertificatesRepository,
extendedOnboardingFeatureToggles = mockExtendedOnboardingFeatureToggles,
userBrowserProperties = mockUserBrowserProperties,
history = mockNavigationHistory,
)
Expand Down Expand Up @@ -5456,6 +5455,7 @@ class BrowserTabViewModelTest {
}

private suspend fun givenFireButtonPulsing() {
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockEnabledToggle)
whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
dismissedCtaDaoChannel.send(listOf(DismissedCta(CtaId.DAX_DIALOG_TRACKERS_FOUND)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class CtaViewModelTest {
private lateinit var testee: CtaViewModel

val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
val mockEnabledToggle: Toggle = mock { on { it.isEnabled() } doReturn true }

@Before
fun before() {
Expand All @@ -138,8 +139,8 @@ class CtaViewModelTest {
.allowMainThreadQueries()
.build()

val mockToggle: Toggle = mock { on { it.isEnabled() } doReturn true }
whenever(mockExtendedOnboardingFeatureToggles.aestheticUpdates()).thenReturn(mockToggle)
val mockDisabledToggle: Toggle = mock { on { it.isEnabled() } doReturn false }
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockDisabledToggle)
whenever(mockAppInstallStore.installTimestamp).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))
whenever(mockUserAllowListRepository.isDomainInUserAllowList(any())).thenReturn(false)
whenever(mockDismissedCtaDao.dismissedCtas()).thenReturn(db.dismissedCtaDao().dismissedCtas())
Expand Down Expand Up @@ -718,6 +719,48 @@ class CtaViewModelTest {
verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_END))
}

@Test
fun givenNoBrowserCtasExperimentWhenRefreshCtaOnHomeTabThenSkipOnboardingHomeCtas() = runTest {
givenDaxOnboardingActive()
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockEnabledToggle)
whenever(mockDismissedCtaDao.exists(CtaId.DAX_INTRO)).thenReturn(false)
whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true)

val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false)
assertFalse(value is DaxBubbleCta.DaxIntroSearchOptionsCta)
}

@Test
fun givenNoBrowserCtasExperimentWhenFirstRefreshCtaOnHomeTabThenDontReturnWidgetCta() = runTest {
givenDaxOnboardingActive()
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockEnabledToggle)
whenever(mockDismissedCtaDao.exists(CtaId.DAX_INTRO)).thenReturn(false)

val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false)
assertFalse(value is HomePanelCta.AddWidgetAuto)
}

@Test
fun givenNoBrowserCtasExperimentWhenRefreshCtaOnHomeTabAndIntroShownThenReturnWidgetCta() = runTest {
givenDaxOnboardingActive()
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockEnabledToggle)
whenever(mockDismissedCtaDao.exists(CtaId.DAX_INTRO)).thenReturn(true)
whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true)

val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false)
assertTrue(value is HomePanelCta.AddWidgetAuto)
}

@Test
fun givenNoBrowserCtasExperimentWhenRefreshCtaWhileBrowsingThenReturnNull() = runTest {
givenDaxOnboardingActive()
whenever(mockExtendedOnboardingFeatureToggles.noBrowserCtas()).thenReturn(mockEnabledToggle)
val site = site(url = "http://www.facebook.com", entity = TestEntity("Facebook", "Facebook", 9.0))

val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site)
assertNull(value)
}

private suspend fun givenDaxOnboardingActive() {
whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
}
Expand Down
42 changes: 17 additions & 25 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ import com.duckduckgo.app.global.model.domainMatchesUrl
import com.duckduckgo.app.location.GeoLocationPermissions
import com.duckduckgo.app.location.data.LocationPermissionType
import com.duckduckgo.app.location.data.LocationPermissionsRepository
import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.ExtendedOnboardingFeatureToggles
import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.OnboardingExperimentPixel
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.pixels.AppPixelName.AUTOCOMPLETE_BANNER_DISMISSED
Expand Down Expand Up @@ -264,7 +263,6 @@ class BrowserTabViewModel @Inject constructor(
private val privacyProtectionsToggleUsageListener: PrivacyProtectionsToggleUsageListener,
private val privacyProtectionsPopupExperimentExternalPixels: PrivacyProtectionsPopupExperimentExternalPixels,
private val faviconsFetchingPrompt: FaviconsFetchingPrompt,
private val extendedOnboardingFeatureToggles: ExtendedOnboardingFeatureToggles,
private val subscriptions: Subscriptions,
private val sslCertificatesFeature: SSLCertificatesFeature,
private val bypassedSSLCertificatesRepository: BypassedSSLCertificatesRepository,
Expand Down Expand Up @@ -3174,12 +3172,10 @@ class BrowserTabViewModel @Inject constructor(
return when (onboardingCta) {
is OnboardingDaxDialogCta.DaxSerpCta -> {
viewModelScope.launch {
if (extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled()) {
val cta = withContext(dispatchers.io()) { ctaViewModel.getSiteSuggestionsDialogCta() }
ctaViewState.value = currentCtaViewState().copy(cta = cta)
if (cta == null) {
command.value = HideOnboardingDaxDialog(onboardingCta)
}
val cta = withContext(dispatchers.io()) { ctaViewModel.getSiteSuggestionsDialogCta() }
ctaViewState.value = currentCtaViewState().copy(cta = cta)
if (cta == null) {
command.value = HideOnboardingDaxDialog(onboardingCta)
}
}
null
Expand All @@ -3193,12 +3189,10 @@ class BrowserTabViewModel @Inject constructor(
browserViewState.value = currentBrowserViewState().copy(showPrivacyShield = HighlightableButton.Visible(highlighted = false))
}
viewModelScope.launch {
if (extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled()) {
val cta = withContext(dispatchers.io()) { ctaViewModel.getFireDialogCta() }
ctaViewState.value = currentCtaViewState().copy(cta = cta)
if (cta == null) {
command.value = HideOnboardingDaxDialog(onboardingCta)
}
val cta = withContext(dispatchers.io()) { ctaViewModel.getFireDialogCta() }
ctaViewState.value = currentCtaViewState().copy(cta = cta)
if (cta == null) {
command.value = HideOnboardingDaxDialog(onboardingCta)
}
}
null
Expand All @@ -3218,22 +3212,20 @@ class BrowserTabViewModel @Inject constructor(
}

fun onFireMenuSelected() {
if (extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled()) {
val cta = currentCtaViewState().cta
if (cta is OnboardingDaxDialogCta.DaxFireButtonCta) {
onUserDismissedCta()
command.value = HideOnboardingDaxDialog(cta)
}
if (currentBrowserViewState().fireButton.isHighlighted()) {
viewModelScope.launch {
ctaViewModel.dismissPulseAnimation()
}
val cta = currentCtaViewState().cta
if (cta is OnboardingDaxDialogCta.DaxFireButtonCta) {
onUserDismissedCta()
command.value = HideOnboardingDaxDialog(cta)
}
if (currentBrowserViewState().fireButton.isHighlighted()) {
viewModelScope.launch {
ctaViewModel.dismissPulseAnimation()
}
}
}

fun onPrivacyShieldSelected() {
if (extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled() && currentBrowserViewState().showPrivacyShield.isHighlighted()) {
if (currentBrowserViewState().showPrivacyShield.isHighlighted()) {
browserViewState.value = currentBrowserViewState().copy(showPrivacyShield = HighlightableButton.Visible(highlighted = false))
pixel.fire(
pixel = PrivacyDashboardPixels.PRIVACY_DASHBOARD_FIRST_TIME_OPENED,
Expand Down
75 changes: 41 additions & 34 deletions app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,20 @@ class CtaViewModel @Inject constructor(
}

private suspend fun getHomeCta(): Cta? {
val onboardingEnabled = extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled()
return when {
canShowDaxIntroCta() && onboardingEnabled -> {
canShowDaxIntroCta() && extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() -> {
dismissedCtaDao.insert(DismissedCta(CtaId.DAX_INTRO))
null
}
canShowDaxIntroCta() && !extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() -> {
DaxBubbleCta.DaxIntroSearchOptionsCta(onboardingStore, appInstallStore)
}

canShowDaxIntroVisitSiteCta() && onboardingEnabled -> {
canShowDaxIntroVisitSiteCta() && !extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() -> {
DaxBubbleCta.DaxIntroVisitSiteOptionsCta(onboardingStore, appInstallStore)
}

canShowDaxCtaEndOfJourney() && onboardingEnabled -> {
canShowDaxCtaEndOfJourney() && !extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() -> {
DaxBubbleCta.DaxEndCta(onboardingStore, appInstallStore)
}

Expand Down Expand Up @@ -247,10 +250,15 @@ class CtaViewModel @Inject constructor(
(daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogSerpShown() || daxDialogTrackersFoundShown())

private suspend fun canShowDaxDialogCta(): Boolean {
if (!daxOnboardingActive() || hideTips()) {
return false
return when {
!daxOnboardingActive() || hideTips() -> false
extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() -> {
settingsDataStore.hideTips = true
userStageStore.stageCompleted(AppStage.DAX_ONBOARDING)
false
}
else -> true
}
return true
}

@WorkerThread
Expand All @@ -269,40 +277,39 @@ class CtaViewModel @Inject constructor(

if (!canShowDaxDialogCta()) return null

if (extendedOnboardingFeatureToggles.aestheticUpdates().isEnabled()) {
// Trackers blocked
if (!daxDialogTrackersFoundShown() && !isSerpUrl(it.url) && it.orderedTrackerBlockedEntities().isNotEmpty()) {
return OnboardingDaxDialogCta.DaxTrackersBlockedCta(
onboardingStore,
appInstallStore,
it.orderedTrackerBlockedEntities(),
)
}
// Trackers blocked
if (!daxDialogTrackersFoundShown() && !isSerpUrl(it.url) && it.orderedTrackerBlockedEntities().isNotEmpty()) {
return OnboardingDaxDialogCta.DaxTrackersBlockedCta(
onboardingStore,
appInstallStore,
it.orderedTrackerBlockedEntities(),
)
}

// Is major network
if (it.entity != null) {
it.entity?.let { entity ->
if (!daxDialogNetworkShown() && OnboardingDaxDialogCta.mainTrackerNetworks.contains(entity.displayName)) {
return OnboardingDaxDialogCta.DaxMainNetworkCta(onboardingStore, appInstallStore, entity.displayName, host)
}
// Is major network
if (it.entity != null) {
it.entity?.let { entity ->
if (!daxDialogNetworkShown() && OnboardingDaxDialogCta.mainTrackerNetworks.contains(entity.displayName)) {
return OnboardingDaxDialogCta.DaxMainNetworkCta(onboardingStore, appInstallStore, entity.displayName, host)
}
}
}

// SERP
if (isSerpUrl(it.url) && !daxDialogSerpShown()) {
return OnboardingDaxDialogCta.DaxSerpCta(onboardingStore, appInstallStore)
}
// SERP
if (isSerpUrl(it.url) && !daxDialogSerpShown()) {
return OnboardingDaxDialogCta.DaxSerpCta(onboardingStore, appInstallStore)
}

// No trackers blocked
if (!isSerpUrl(it.url) && !daxDialogOtherShown() && !daxDialogTrackersFoundShown() && !daxDialogNetworkShown()) {
return OnboardingDaxDialogCta.DaxNoTrackersCta(onboardingStore, appInstallStore)
}
// No trackers blocked
if (!isSerpUrl(it.url) && !daxDialogOtherShown() && !daxDialogTrackersFoundShown() && !daxDialogNetworkShown()) {
return OnboardingDaxDialogCta.DaxNoTrackersCta(onboardingStore, appInstallStore)
}

// End
if (canShowDaxCtaEndOfJourney() && daxDialogFireEducationShown()) {
return OnboardingDaxDialogCta.DaxEndCta(onboardingStore, appInstallStore)
}
// End
if (canShowDaxCtaEndOfJourney() && daxDialogFireEducationShown()) {
return OnboardingDaxDialogCta.DaxEndCta(onboardingStore, appInstallStore)
}

return null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.duckduckgo.app.onboarding.ui.page.extendedonboarding
import com.duckduckgo.anvil.annotations.ContributesRemoteFeature
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.Toggle
import com.duckduckgo.feature.toggles.api.Toggle.Experiment

@ContributesRemoteFeature(
scope = AppScope::class,
Expand All @@ -31,4 +32,8 @@ interface ExtendedOnboardingFeatureToggles {

@Toggle.DefaultValue(true)
fun aestheticUpdates(): Toggle

@Toggle.DefaultValue(false)
@Experiment
fun noBrowserCtas(): Toggle
}