Skip to content

Commit

Permalink
Update Settings: Add new pixels (#5434)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1207908166761516/1208880593716452/f

### Description

Adds new pixels to Settings where we were missing some compared to iOS.
See task for more details.

### Steps to test this PR

Prerequisite: Enable `newSettings` feature toggle

_Cookie Pop-Up Protection_
- [x] Open Cookie Pop-Up Protection screen
- [x] Check `m_settings_autoconsent_shown` Pixel is fired
- [x] Toggle pop-up protection off
- [x] Check `m_settings_autoconsent_off` Pixel is fired
- [x] Toggle pop-up protection on
- [x] Check `m_settings_autoconsent_on` Pixel is fired

_Next Steps: Address Bar Position_
- [x] From main Settings screen click "Set Your Address Bar Position" 
- [x] Check `m_settings_next_steps_set_address_bar` Pixel is fired

_Next Steps: Enable Voice Search_
- [x] Use a Pixel device
- [x] From main Settings screen click "Enable Voice Search" 
- [x] Check `m_settings_next_steps_enable_voice_search` Pixel is fired

### UI changes
N/A
  • Loading branch information
mikescamell authored Jan 7, 2025
1 parent de18113 commit 7a9ba7c
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 3 deletions.
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
}
}
}
}

0 comments on commit 7a9ba7c

Please sign in to comment.