Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Settings: Add new pixels #5434

Merged
merged 4 commits into from
Jan 7, 2025
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
2 changes: 2 additions & 0 deletions app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName {
SETTINGS_ADDRESS_BAR_POSITION_PRESSED("ms_address_bar_position_setting_pressed"),
SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP("ms_address_bar_position_setting_selected_top"),
SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM("ms_address_bar_position_setting_selected_bottom"),
SETTINGS_NEXT_STEPS_ADDRESS_BAR("m_settings_next_steps_set_address_bar"),
SETTINGS_NEXT_STEPS_VOICE_SEARCH("m_settings_next_steps_enable_voice_search"),
SETTINGS_MAC_APP_PRESSED("ms_mac_app_setting_pressed"),
SETTINGS_WINDOWS_APP_PRESSED("ms_windows_app_setting_pressed"),
SETTINGS_EMAIL_PROTECTION_PRESSED("ms_email_protection_setting_pressed"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_DEFAULT_BROWSER_PRESSED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_EMAIL_PROTECTION_PRESSED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_FIRE_BUTTON_PRESSED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_GENERAL_PRESSED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_NEXT_STEPS_ADDRESS_BAR
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_NEXT_STEPS_VOICE_SEARCH
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_OPENED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_PERMISSIONS_PRESSED
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_PRIVATE_SEARCH_PRESSED
Expand Down Expand Up @@ -213,10 +215,12 @@ class NewSettingsViewModel @Inject constructor(

fun onChangeAddressBarPositionClicked() {
viewModelScope.launch { command.send(LaunchAppearanceScreen) }
pixel.fire(SETTINGS_NEXT_STEPS_ADDRESS_BAR)
}

fun onEnableVoiceSearchClicked() {
viewModelScope.launch { command.send(LaunchAccessibilitySettings) }
pixel.fire(SETTINGS_NEXT_STEPS_VOICE_SEARCH)
}

fun onDefaultBrowserSettingClicked() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autoconsent.impl.pixels

import com.duckduckgo.app.statistics.pixels.Pixel

enum class AutoConsentPixel(override val pixelName: String) : Pixel.PixelName {

SETTINGS_AUTOCONSENT_SHOWN("m_settings_autoconsent_shown"),
SETTINGS_AUTOCONSENT_ON("m_settings_autoconsent_on"),
SETTINGS_AUTOCONSENT_OFF("m_settings_autoconsent_off"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.duckduckgo.anvil.annotations.ContributesViewModel
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.autoconsent.api.Autoconsent
import com.duckduckgo.autoconsent.impl.R
import com.duckduckgo.autoconsent.impl.pixels.AutoConsentPixel.SETTINGS_AUTOCONSENT_OFF
import com.duckduckgo.autoconsent.impl.pixels.AutoConsentPixel.SETTINGS_AUTOCONSENT_ON
import com.duckduckgo.autoconsent.impl.pixels.AutoConsentPixel.SETTINGS_AUTOCONSENT_SHOWN
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.settings.api.NewSettingsFeature
import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
Expand All @@ -33,7 +38,11 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch

@ContributesViewModel(ActivityScope::class)
class AutoconsentSettingsViewModel @Inject constructor(private val autoconsent: Autoconsent) : ViewModel() {
class AutoconsentSettingsViewModel @Inject constructor(
private val autoconsent: Autoconsent,
private val pixel: Pixel,
private val newSettingsFeature: NewSettingsFeature,
) : ViewModel() {
data class ViewState(
val autoconsentEnabled: Boolean,
)
Expand All @@ -47,12 +56,27 @@ class AutoconsentSettingsViewModel @Inject constructor(private val autoconsent:
MutableStateFlow(ViewState(autoconsent.isSettingEnabled()))
val viewState: StateFlow<ViewState> = viewStateFlow

init {
if (newSettingsFeature.self().isEnabled()) {
pixel.fire(SETTINGS_AUTOCONSENT_SHOWN)
}
}

fun commands(): Flow<Command> {
return command.receiveAsFlow()
}

fun onUserToggleAutoconsent(enabled: Boolean) {
viewModelScope.launch {
if (newSettingsFeature.self().isEnabled()) {
pixel.fire(
if (enabled) {
SETTINGS_AUTOCONSENT_ON
} else {
SETTINGS_AUTOCONSENT_OFF
},
)
}
autoconsent.changeSetting(enabled)
viewStateFlow.emit(ViewState(autoconsent.isSettingEnabled()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@ package com.duckduckgo.autoconsent.impl.ui

import android.webkit.WebView
import app.cash.turbine.test
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelName
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType
import com.duckduckgo.autoconsent.api.Autoconsent
import com.duckduckgo.autoconsent.api.AutoconsentCallback
import com.duckduckgo.autoconsent.impl.pixels.AutoConsentPixel
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.feature.toggles.api.Toggle
import com.duckduckgo.feature.toggles.api.Toggle.FeatureName
import com.duckduckgo.feature.toggles.api.Toggle.State
import com.duckduckgo.feature.toggles.api.Toggle.State.Cohort
import com.duckduckgo.feature.toggles.api.Toggle.State.CohortName
import com.duckduckgo.settings.api.NewSettingsFeature
import kotlinx.coroutines.test.runTest
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test

Expand All @@ -32,11 +43,31 @@ class AutoconsentSettingsViewModelTest {
var coroutineRule = CoroutineTestRule()

private val autoconsent: Autoconsent = FakeAutoconsent()
private val pixel: FakePixel = FakePixel()
private val newSettingsFeature: FakeNewSettingsFeature = FakeNewSettingsFeature()

private val viewModel = AutoconsentSettingsViewModel(autoconsent)
private lateinit var viewModel: AutoconsentSettingsViewModel

@Before
fun setup() {
newSettingsFeature.enabled = false
pixel.firedPixels.clear()
}

@Test
fun whenViewModelCreatedThenAutoConsentShownPixelFired() {
newSettingsFeature.enabled = true

initViewModel()

assertEquals(1, pixel.firedPixels.size)
assertEquals(AutoConsentPixel.SETTINGS_AUTOCONSENT_SHOWN.pixelName, pixel.firedPixels.first())
}

@Test
fun whenViewModelCreatedThenEmitViewState() = runTest {
initViewModel()

viewModel.viewState.test {
assertFalse(awaitItem().autoconsentEnabled)
cancelAndIgnoreRemainingEvents()
Expand All @@ -45,6 +76,8 @@ class AutoconsentSettingsViewModelTest {

@Test
fun whenOnLearnMoreSelectedCalledThenLaunchLearnMoreWebPageCommandIsSent() = runTest {
initViewModel()

viewModel.commands().test {
viewModel.onLearnMoreSelected()
assertEquals(AutoconsentSettingsViewModel.Command.LaunchLearnMoreWebPage(), awaitItem())
Expand All @@ -54,6 +87,8 @@ class AutoconsentSettingsViewModelTest {

@Test
fun whenOnUserToggleAutoconsentToTrueThenAutoconsentEnabledIsTrue() = runTest {
initViewModel()

viewModel.viewState.test {
assertFalse(awaitItem().autoconsentEnabled)
viewModel.onUserToggleAutoconsent(true)
Expand All @@ -63,19 +98,52 @@ class AutoconsentSettingsViewModelTest {
}
}

@Test
fun whenOnUserToggleAutoconsentToTrueThenAutoconsentOnPixelIsFired() {
newSettingsFeature.enabled = true

initViewModel()

viewModel.onUserToggleAutoconsent(true)

assertEquals(2, pixel.firedPixels.size)
assertEquals(AutoConsentPixel.SETTINGS_AUTOCONSENT_ON.pixelName, pixel.firedPixels[1])
}

@Test
fun whenOnUserToggleAutoconsentToFalseThenAutoconsentEnabledIsFalse() = runTest {
initViewModel()

viewModel.viewState.test {
viewModel.onUserToggleAutoconsent(false)
assertFalse(awaitItem().autoconsentEnabled)
cancelAndIgnoreRemainingEvents()
}
}

@Test
fun whenOnUserToggleAutoconsentToTrueThenAutoconsentOffPixelIsFired() {
newSettingsFeature.enabled = true

initViewModel()

viewModel.onUserToggleAutoconsent(false)

assertEquals(2, pixel.firedPixels.size)
assertEquals(AutoConsentPixel.SETTINGS_AUTOCONSENT_OFF.pixelName, pixel.firedPixels[1])
}

private fun initViewModel() {
viewModel = AutoconsentSettingsViewModel(autoconsent, pixel, newSettingsFeature)
}

internal class FakeAutoconsent : Autoconsent {
var test: Boolean = false

override fun injectAutoconsent(webView: WebView, url: String) {
override fun injectAutoconsent(
webView: WebView,
url: String,
) {
// NO OP
}

Expand Down Expand Up @@ -108,4 +176,75 @@ class AutoconsentSettingsViewModelTest {
// NO OP
}
}

internal class FakePixel : Pixel {

val firedPixels = mutableListOf<String>()

override fun fire(
pixel: PixelName,
parameters: Map<String, String>,
encodedParameters: Map<String, String>,
type: PixelType,
) {
firedPixels.add(pixel.pixelName)
}

override fun fire(
pixelName: String,
parameters: Map<String, String>,
encodedParameters: Map<String, String>,
type: PixelType,
) {
firedPixels.add(pixelName)
}

override fun enqueueFire(
pixel: PixelName,
parameters: Map<String, String>,
encodedParameters: Map<String, String>,
) {
firedPixels.add(pixel.pixelName)
}

override fun enqueueFire(
pixelName: String,
parameters: Map<String, String>,
encodedParameters: Map<String, String>,
) {
firedPixels.add(pixelName)
}
}

internal class FakeNewSettingsFeature : NewSettingsFeature {

var enabled: Boolean = false

override fun self(): Toggle = object : Toggle {

override fun featureName(): FeatureName {
return FeatureName(null, "FakeNewSettingsFeature")
}

override fun isEnabled(cohort: CohortName): Boolean {
return enabled
}

override fun setRawStoredState(state: State) {
// NO OP
}

override fun getRawStoredState(): State? {
return null
}

override fun getSettings(): String? {
return null
}

override fun getCohort(): Cohort? {
return null
}
}
}
}
Loading