Dan Ballard pushed to branch tor-browser-128.8.0esr-14.0-1 at The Tor Project / Applications / Tor Browser

Commits:

6 changed files:

Changes:

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
    ... ... @@ -12,33 +12,65 @@ import android.content.res.Configuration
    12 12
     import android.graphics.drawable.ColorDrawable
    
    13 13
     import android.net.Uri
    
    14 14
     import android.os.Bundle
    
    15
    +import android.util.Log
    
    15 16
     import android.view.LayoutInflater
    
    16 17
     import android.view.View
    
    17 18
     import android.view.ViewGroup
    
    19
    +import android.widget.ImageView
    
    18 20
     import androidx.activity.result.ActivityResultLauncher
    
    19 21
     import androidx.annotation.VisibleForTesting
    
    22
    +import androidx.compose.foundation.Image
    
    23
    +import androidx.compose.foundation.background
    
    24
    +import androidx.compose.foundation.border
    
    25
    +import androidx.compose.foundation.layout.Arrangement
    
    26
    +import androidx.compose.foundation.layout.Box
    
    27
    +import androidx.compose.foundation.layout.BoxWithConstraints
    
    20 28
     import androidx.compose.foundation.layout.Column
    
    29
    +import androidx.compose.foundation.layout.Row
    
    30
    +import androidx.compose.foundation.layout.Spacer
    
    21 31
     import androidx.compose.foundation.layout.fillMaxWidth
    
    22 32
     import androidx.compose.foundation.layout.heightIn
    
    23 33
     import androidx.compose.foundation.layout.padding
    
    34
    +import androidx.compose.foundation.layout.size
    
    35
    +import androidx.compose.foundation.layout.wrapContentHeight
    
    36
    +import androidx.compose.foundation.layout.wrapContentSize
    
    37
    +import androidx.compose.foundation.rememberScrollState
    
    38
    +import androidx.compose.foundation.shape.CircleShape
    
    24 39
     import androidx.compose.foundation.shape.RoundedCornerShape
    
    40
    +import androidx.compose.foundation.verticalScroll
    
    41
    +import androidx.compose.material.Button
    
    25 42
     import androidx.compose.material.ButtonDefaults
    
    43
    +import androidx.compose.material.Icon
    
    44
    +import androidx.compose.material.IconButton
    
    26 45
     import androidx.compose.material.Text
    
    27 46
     import androidx.compose.material.TextButton
    
    47
    +import androidx.compose.runtime.Composable
    
    48
    +import androidx.compose.runtime.mutableStateOf
    
    49
    +import androidx.compose.runtime.remember
    
    50
    +import androidx.compose.ui.Alignment
    
    28 51
     import androidx.compose.ui.ExperimentalComposeUiApi
    
    29 52
     import androidx.compose.ui.Modifier
    
    53
    +import androidx.compose.ui.draw.clip
    
    54
    +import androidx.compose.ui.draw.drawBehind
    
    55
    +import androidx.compose.ui.draw.scale
    
    56
    +import androidx.compose.ui.graphics.Color
    
    57
    +import androidx.compose.ui.layout.ContentScale
    
    30 58
     import androidx.compose.ui.platform.ViewCompositionStrategy
    
    59
    +import androidx.compose.ui.res.painterResource
    
    60
    +import androidx.compose.ui.semantics.heading
    
    31 61
     import androidx.compose.ui.semantics.semantics
    
    32 62
     import androidx.compose.ui.semantics.testTag
    
    33 63
     import androidx.compose.ui.semantics.testTagsAsResourceId
    
    64
    +import androidx.compose.ui.text.TextLayoutResult
    
    65
    +import androidx.compose.ui.text.font.FontWeight
    
    34 66
     import androidx.compose.ui.text.style.TextAlign
    
    67
    +import androidx.compose.ui.unit.Dp
    
    35 68
     import androidx.compose.ui.unit.dp
    
    69
    +import androidx.compose.ui.unit.sp
    
    36 70
     import androidx.compose.ui.viewinterop.AndroidView
    
    37 71
     import androidx.coordinatorlayout.widget.CoordinatorLayout
    
    38 72
     import androidx.core.content.ContextCompat
    
    39 73
     import androidx.core.content.ContextCompat.getColor
    
    40
    -import androidx.core.view.children
    
    41
    -import androidx.core.view.doOnLayout
    
    42 74
     import androidx.core.view.isGone
    
    43 75
     import androidx.core.view.isVisible
    
    44 76
     import androidx.fragment.app.Fragment
    
    ... ... @@ -108,6 +140,7 @@ import org.mozilla.fenix.components.FenixSnackbar
    108 140
     import org.mozilla.fenix.components.PrivateShortcutCreateManager
    
    109 141
     import org.mozilla.fenix.components.TabCollectionStorage
    
    110 142
     import org.mozilla.fenix.components.appstate.AppAction
    
    143
    +import org.mozilla.fenix.components.components
    
    111 144
     import org.mozilla.fenix.components.menu.MenuAccessPoint
    
    112 145
     import org.mozilla.fenix.components.toolbar.IncompleteRedesignToolbarFeature
    
    113 146
     import org.mozilla.fenix.components.toolbar.ToolbarPosition
    
    ... ... @@ -151,7 +184,6 @@ import org.mozilla.fenix.messaging.DefaultMessageController
    151 184
     import org.mozilla.fenix.messaging.FenixMessageSurfaceId
    
    152 185
     import org.mozilla.fenix.messaging.MessagingFeature
    
    153 186
     import org.mozilla.fenix.microsurvey.ui.MicrosurveyRequestPrompt
    
    154
    -import org.mozilla.fenix.nimbus.FxNimbus
    
    155 187
     import org.mozilla.fenix.perf.MarkersFragmentLifecycleCallbacks
    
    156 188
     import org.mozilla.fenix.search.toolbar.DefaultSearchSelectorController
    
    157 189
     import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
    
    ... ... @@ -160,12 +192,16 @@ import org.mozilla.fenix.tabstray.TabsTrayAccessPoint
    160 192
     import org.mozilla.fenix.theme.FirefoxTheme
    
    161 193
     import org.mozilla.fenix.tor.TorBootstrapFragmentDirections
    
    162 194
     import org.mozilla.fenix.tor.TorBootstrapStatus
    
    195
    +import org.mozilla.fenix.tor.CampaignStrings
    
    163 196
     import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHOLD
    
    164 197
     import org.mozilla.fenix.utils.allowUndo
    
    165 198
     import org.mozilla.fenix.wallpapers.Wallpaper
    
    166 199
     import java.lang.ref.WeakReference
    
    167 200
     import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics
    
    168 201
     
    
    202
    +import java.text.SimpleDateFormat
    
    203
    +import java.util.Date
    
    204
    +
    
    169 205
     @Suppress("TooManyFunctions", "LargeClass")
    
    170 206
     class HomeFragment : Fragment(), UserInteractionHandler {
    
    171 207
         private val args by navArgs<HomeFragmentArgs>()
    
    ... ... @@ -513,6 +549,8 @@ class HomeFragment : Fragment(), UserInteractionHandler {
    513 549
     
    
    514 550
             activity.themeManager.applyStatusBarTheme(activity)
    
    515 551
     
    
    552
    +        tryShowUX2025Survey()
    
    553
    +
    
    516 554
             // FxNimbus.features.homescreen.recordExposure()
    
    517 555
     
    
    518 556
             // DO NOT MOVE ANYTHING BELOW THIS addMarker CALL!
    
    ... ... @@ -524,6 +562,31 @@ class HomeFragment : Fragment(), UserInteractionHandler {
    524 562
             return binding.root
    
    525 563
         }
    
    526 564
     
    
    565
    +    private fun tryShowUX2025Survey() {
    
    566
    +        val allowedLocales = arrayListOf("en", "es", "ru", "fr", "pt")
    
    567
    +        val locale = CampaignStrings.getLocale()
    
    568
    +
    
    569
    +        val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz")
    
    570
    +        val startDate = dateFormat.parse("2025-04-14-12-UTC")
    
    571
    +
    
    572
    +        val endDate = dateFormat.parse("2025-04-28-00-UTC")
    
    573
    +        val currentDate = Date()
    
    574
    +
    
    575
    +        if (currentDate.before(startDate) || currentDate.after(endDate)) {
    
    576
    +            return // comment out to test
    
    577
    +        }
    
    578
    +
    
    579
    +        if (allowedLocales.contains(locale) && !requireContext().settings().hideCampaign) {
    
    580
    +            binding.onionPatternImage.visibility = View.GONE
    
    581
    +            binding.campaignBox.apply {
    
    582
    +                setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
    
    583
    +                setContent {
    
    584
    +                    CampaignBox()
    
    585
    +                }
    
    586
    +            }
    
    587
    +        }
    
    588
    +    }
    
    589
    +
    
    527 590
         private fun reinitializeNavBar() {
    
    528 591
             initializeNavBar(activity = requireActivity() as HomeActivity)
    
    529 592
         }
    
    ... ... @@ -1402,4 +1465,213 @@ class HomeFragment : Fragment(), UserInteractionHandler {
    1402 1465
             requireActivity().finish()
    
    1403 1466
             return true
    
    1404 1467
         }
    
    1468
    +
    
    1469
    +    @Composable
    
    1470
    +    fun CampaignBox() {
    
    1471
    +        BoxWithConstraints(
    
    1472
    +            contentAlignment = Alignment.Center,
    
    1473
    +            modifier = Modifier
    
    1474
    +                .fillMaxWidth()
    
    1475
    +                .wrapContentHeight()
    
    1476
    +        ) {
    
    1477
    +            val alternateLayout = this.maxWidth >= 500.dp
    
    1478
    +
    
    1479
    +            CampaignLayout(
    
    1480
    +                alternateLayout,
    
    1481
    +                maxWidth = this.maxWidth,
    
    1482
    +                modifier = Modifier
    
    1483
    +                    .padding(top = if (alternateLayout) 65.dp else 55.dp, bottom = 56.dp),
    
    1484
    +            )
    
    1485
    +        }
    
    1486
    +    }
    
    1487
    +
    
    1488
    +    @Composable
    
    1489
    +    private fun CampaignLayout(
    
    1490
    +        alternateLayout: Boolean,
    
    1491
    +        maxWidth: Dp,
    
    1492
    +        modifier: Modifier
    
    1493
    +    ) {
    
    1494
    +        Column(
    
    1495
    +            modifier = modifier
    
    1496
    +                .padding(horizontal = 22.dp)
    
    1497
    +                .verticalScroll(rememberScrollState())
    
    1498
    +                .fillMaxWidth(getVariableWidth(maxWidth)),
    
    1499
    +            horizontalAlignment = Alignment.CenterHorizontally,
    
    1500
    +        ) {
    
    1501
    +            PurpleBox(alternateLayout)
    
    1502
    +        }
    
    1503
    +    }
    
    1504
    +
    
    1505
    +    private fun getVariableWidth(width: Dp): Float = (500.dp / width).coerceIn(0.75f, 1.0f)
    
    1506
    +
    
    1507
    +    @Composable
    
    1508
    +    private fun PurpleBox(
    
    1509
    +        alternateLayout: Boolean,
    
    1510
    +    ) {
    
    1511
    +        Box(
    
    1512
    +            modifier = Modifier.background(PhotonColors.Violet90, shape = RoundedCornerShape(8.dp))
    
    1513
    +                .padding(16.dp),
    
    1514
    +        ) {
    
    1515
    +            Row(
    
    1516
    +                modifier = Modifier.fillMaxWidth(),
    
    1517
    +            ) {
    
    1518
    +                Emoji()
    
    1519
    +                Spacer(Modifier.weight(1f))
    
    1520
    +                ExitIcon()
    
    1521
    +            }
    
    1522
    +            DynamicCampaignContent(alternateLayout)
    
    1523
    +        }
    
    1524
    +    }
    
    1525
    +
    
    1526
    +    @Composable
    
    1527
    +    private fun Emoji() {
    
    1528
    +        val alpha38Violet40 = Color(PhotonColors.Violet40.red, PhotonColors.Violet40.green, PhotonColors.Violet40.blue, 0.38f)
    
    1529
    +        Image(
    
    1530
    +            painter = painterResource(id = R.drawable.campaign_hand),
    
    1531
    +            contentDescription = null,
    
    1532
    +            modifier = Modifier
    
    1533
    +                .size(64.dp)
    
    1534
    +                .padding(16.dp)
    
    1535
    +                .drawBehind {
    
    1536
    +                    drawCircle(
    
    1537
    +                        color = alpha38Violet40,
    
    1538
    +                        radius = this.size.maxDimension
    
    1539
    +                    )
    
    1540
    +                }
    
    1541
    +        )
    
    1542
    +    }
    
    1543
    +
    
    1544
    +    @Composable
    
    1545
    +    private fun ExitIcon() {
    
    1546
    +        IconButton(
    
    1547
    +            onClick = {
    
    1548
    +                binding.campaignBox.visibility = View.GONE
    
    1549
    +                binding.onionPatternImage.visibility = View.VISIBLE
    
    1550
    +                context?.components?.settings?.hideCampaign = true
    
    1551
    +            },
    
    1552
    +        ) {
    
    1553
    +            Icon(
    
    1554
    +                painter = painterResource(id = R.drawable.ic_close),
    
    1555
    +                tint = Color(
    
    1556
    +                    getColor(
    
    1557
    +                        requireContext(),
    
    1558
    +                        R.color.photonWhite,
    
    1559
    +                    ),
    
    1560
    +                ),
    
    1561
    +                contentDescription = CampaignStrings.get(CampaignStrings.CloseKey),
    
    1562
    +                modifier = Modifier
    
    1563
    +                    .size(48.dp)
    
    1564
    +                    .padding(8.dp)
    
    1565
    +            )
    
    1566
    +        }
    
    1567
    +    }
    
    1568
    +
    
    1569
    +
    
    1570
    +    @Composable
    
    1571
    +    private fun DynamicCampaignContent(
    
    1572
    +        alternateLayout: Boolean
    
    1573
    +    ) {
    
    1574
    +        Row(verticalAlignment = Alignment.CenterVertically) {
    
    1575
    +            Column(
    
    1576
    +                modifier = Modifier.fillMaxWidth()
    
    1577
    +                    .padding( top = 88.dp),
    
    1578
    +                horizontalAlignment = Alignment.Start,
    
    1579
    +            ) {
    
    1580
    +                TitleText()
    
    1581
    +                MainText()
    
    1582
    +
    
    1583
    +                if (alternateLayout) {
    
    1584
    +                    Row(modifier = Modifier.fillMaxWidth()) {
    
    1585
    +                        Button1(alternateLayout)
    
    1586
    +                        Button2()
    
    1587
    +                    }
    
    1588
    +                } else {
    
    1589
    +                    Button1(alternateLayout)
    
    1590
    +                    Button2()
    
    1591
    +                }
    
    1592
    +
    
    1593
    +            }
    
    1594
    +        }
    
    1595
    +    }
    
    1596
    +
    
    1597
    +    @Composable
    
    1598
    +    private fun TitleText() {
    
    1599
    +
    
    1600
    +        Text(text = CampaignStrings.get(CampaignStrings.HeaderKey),
    
    1601
    +            color = PhotonColors.LightGrey05,
    
    1602
    +            textAlign = TextAlign.Left,
    
    1603
    +            fontWeight = FontWeight.Bold,
    
    1604
    +            fontSize = 24.sp,
    
    1605
    +            lineHeight = 34.sp,
    
    1606
    +            modifier =  Modifier.padding(bottom = 16.dp)
    
    1607
    +        )
    
    1608
    +    }
    
    1609
    +
    
    1610
    +    @Composable
    
    1611
    +    private fun MainText() {
    
    1612
    +
    
    1613
    +        Text(text =  CampaignStrings.get(CampaignStrings.BodyKey),
    
    1614
    +            modifier = Modifier
    
    1615
    +                .fillMaxWidth()
    
    1616
    +                .padding(
    
    1617
    +                    start = 0.dp,
    
    1618
    +                    end = 0.dp,
    
    1619
    +                    bottom = 18.dp,
    
    1620
    +                ),
    
    1621
    +            color = PhotonColors.LightGrey05,
    
    1622
    +            fontSize = 18.sp,
    
    1623
    +            textAlign = TextAlign.Left,
    
    1624
    +            )
    
    1625
    +    }
    
    1626
    +
    
    1627
    +    @Composable
    
    1628
    +    private fun Button1(alternateLayout: Boolean) {
    
    1629
    +        Button(
    
    1630
    +            onClick = {
    
    1631
    +                var locale = CampaignStrings.getLocale()
    
    1632
    +                if (locale == "pt") {
    
    1633
    +                    locale = "pt-BR"
    
    1634
    +                }
    
    1635
    +                (activity as HomeActivity).openToBrowserAndLoad(
    
    1636
    +                    searchTermOrURL = "https://survey.torproject.org/index.php/923269?lang=${locale}",
    
    1637
    +                    newTab = true,
    
    1638
    +                    from = BrowserDirection.FromHome,
    
    1639
    +                )
    
    1640
    +            },
    
    1641
    +            colors = ButtonDefaults.buttonColors(
    
    1642
    +                backgroundColor = PhotonColors.Violet60),
    
    1643
    +            shape = RoundedCornerShape(4.dp),
    
    1644
    +            modifier = Modifier.padding(0.dp)
    
    1645
    +                .fillMaxWidth(fraction = if (alternateLayout) 0.5f else 1f),
    
    1646
    +
    
    1647
    +        ) {
    
    1648
    +            Text(text = CampaignStrings.get(CampaignStrings.CTAKey),
    
    1649
    +                color = PhotonColors.LightGrey05,
    
    1650
    +                textAlign = TextAlign.Center,
    
    1651
    +                fontSize = 18.sp,
    
    1652
    +                modifier = Modifier.padding(8.dp))
    
    1653
    +        }
    
    1654
    +    }
    
    1655
    +
    
    1656
    +    @Composable
    
    1657
    +    private fun Button2() {
    
    1658
    +        Button(
    
    1659
    +            onClick = {
    
    1660
    +                binding.campaignBox.visibility = View.GONE
    
    1661
    +                binding.onionPatternImage.visibility = View.VISIBLE
    
    1662
    +                context?.components?.settings?.hideCampaign = true
    
    1663
    +            },
    
    1664
    +            colors = ButtonDefaults.buttonColors(
    
    1665
    +            backgroundColor = PhotonColors.Violet90),
    
    1666
    +            shape = RoundedCornerShape(4.dp),
    
    1667
    +            modifier = Modifier.padding(0.dp)
    
    1668
    +                .fillMaxWidth()
    
    1669
    +        ) {
    
    1670
    +            Text(text = CampaignStrings.get(CampaignStrings.DismissKey),
    
    1671
    +            color = PhotonColors.Violet20,
    
    1672
    +            textAlign = TextAlign.Center,
    
    1673
    +            fontSize = 18.sp,
    
    1674
    +            modifier = Modifier.padding(8.dp))
    
    1675
    +        }
    
    1676
    +    }
    
    1405 1677
     }

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignStrings.kt
    1
    +package org.mozilla.fenix.tor
    
    2
    +
    
    3
    +import java.util.Locale
    
    4
    +
    
    5
    +object CampaignStrings {
    
    6
    +
    
    7
    +    val HeaderKey = "key_header"
    
    8
    +    val BodyKey = "key_body"
    
    9
    +    val CTAKey = "key_cta"
    
    10
    +    val DismissKey = "key_dismiss"
    
    11
    +    val CloseKey = "key_close"
    
    12
    +
    
    13
    +    private val translations: HashMap<String, HashMap<String, String>> = hashMapOf(
    
    14
    +        "en" to hashMapOf(
    
    15
    +            HeaderKey to "We’d love your feedback",
    
    16
    +            BodyKey to "Help us improve Tor Browser by completing this 10-minute survey.",
    
    17
    +            CTAKey to "Launch the survey",
    
    18
    +            DismissKey to "Dismiss",
    
    19
    +            CloseKey to "Close",
    
    20
    +        ),
    
    21
    +        "es" to hashMapOf(
    
    22
    +            HeaderKey to "Danos tu opinión",
    
    23
    +            BodyKey to "Ayúdanos a mejorar el Navegador Tor completando esta encuesta de 10 minutos.",
    
    24
    +            CTAKey to "Iniciar la encuesta",
    
    25
    +            DismissKey to "Descartar",
    
    26
    +            CloseKey to "Cerrar",
    
    27
    +        ),
    
    28
    +        "ru" to hashMapOf(
    
    29
    +            HeaderKey to "Мы будем рады вашим отзывам",
    
    30
    +            BodyKey to "Помогите нам улучшить браузер Tor, пройдя 10-минутный опрос.",
    
    31
    +            CTAKey to "Начать опрос",
    
    32
    +            DismissKey to "Отклонить",
    
    33
    +            CloseKey to "Закрыть",
    
    34
    +        ),
    
    35
    +        "fr" to hashMapOf(
    
    36
    +            HeaderKey to "Nous serions ravis d’avoir votre avis !",
    
    37
    +            BodyKey to "Aidez-nous à améliorer le navigateur Tor en répondant à cette enquête de 10 minutes.",
    
    38
    +            CTAKey to "Lancer l'enquête",
    
    39
    +            DismissKey to "Ignorer",
    
    40
    +            CloseKey to "Fermer",
    
    41
    +        ),
    
    42
    +        "pt" to hashMapOf(
    
    43
    +            HeaderKey to "Adoraríamos ouvir sua opinião",
    
    44
    +            BodyKey to "Ajude-nos a melhorar o Navegador Tor respondendo a esta pesquisa de 10 minutos.",
    
    45
    +            CTAKey to "Iniciar a pesquisa",
    
    46
    +            DismissKey to "Dispensar",
    
    47
    +            CloseKey to "Fechar"
    
    48
    +        ),
    
    49
    +    )
    
    50
    +
    
    51
    +    fun getLocale(): String {
    
    52
    +        // TODO: do we care about spoofEnglish setting?
    
    53
    +        return Locale.getDefault().getLanguage();
    
    54
    +    }
    
    55
    +
    
    56
    +
    
    57
    +    fun get(key: String): String {
    
    58
    +        val localeStrings = translations.get(getLocale())
    
    59
    +        if (localeStrings == null) {
    
    60
    +            return ""
    
    61
    +        }
    
    62
    +        return localeStrings.get(key) ?: ""
    
    63
    +    }
    
    64
    +}

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
    ... ... @@ -43,15 +43,7 @@ import org.mozilla.fenix.components.settings.lazyFeatureFlagPreference
    43 43
     import org.mozilla.fenix.components.toolbar.ToolbarPosition
    
    44 44
     import org.mozilla.fenix.ext.components
    
    45 45
     import org.mozilla.fenix.ext.getPreferenceKey
    
    46
    -import org.mozilla.fenix.nimbus.CookieBannersSection
    
    47 46
     import org.mozilla.fenix.nimbus.FxNimbus
    
    48
    -import org.mozilla.fenix.nimbus.HomeScreenSection
    
    49
    -import org.mozilla.fenix.nimbus.Mr2022Section
    
    50
    -import org.mozilla.fenix.nimbus.QueryParameterStrippingSection
    
    51
    -import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING
    
    52
    -import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_ALLOW_LIST
    
    53
    -import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_PMB
    
    54
    -import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_STRIP_LIST
    
    55 47
     import org.mozilla.fenix.settings.PhoneFeature
    
    56 48
     import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType
    
    57 49
     import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
    
    ... ... @@ -2111,4 +2103,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
    2111 2103
             appContext.getPreferenceKey(R.string.pref_key_use_html_connection_ui),
    
    2112 2104
             default = false,
    
    2113 2105
         )
    
    2106
    +
    
    2107
    +    var hideCampaign by booleanPreference(
    
    2108
    +        appContext.getPreferenceKey(R.string.pref_key_hide_campaign_2025_ux_survey),
    
    2109
    +        default = false,
    
    2110
    +    )
    
    2114 2111
     }

  • mobile/android/fenix/app/src/main/res/drawable/campaign_hand.xml
    1
    +<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
    
    2
    +    android:viewportWidth="36"
    
    3
    +    android:viewportHeight="36"
    
    4
    +    android:width="36dp"
    
    5
    +    android:height="36dp">
    
    6
    +    <path
    
    7
    +        android:pathData="M4.861 9.147c0.94 -0.657 2.357 -0.531 3.201 0.166l-0.968 -1.407c-0.779 -1.111 -0.5 -2.313 0.612 -3.093 1.112 -0.777 4.263 1.312 4.263 1.312 -0.786 -1.122 -0.639 -2.544 0.483 -3.331 1.122 -0.784 2.67 -0.513 3.456 0.611l10.42 14.72L25 31l-11.083 -4.042L4.25 12.625c-0.793 -1.129 -0.519 -2.686 0.611 -3.478z"
    
    8
    +        android:fillColor="#EF9645" />
    
    9
    +    <path
    
    10
    +        android:pathData="M2.695 17.336s-1.132 -1.65 0.519 -2.781c1.649 -1.131 2.78 0.518 2.78 0.518l5.251 7.658c0.181 -0.302 0.379 -0.6 0.6 -0.894L4.557 11.21s-1.131 -1.649 0.519 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l6.855 9.997c0.255 -0.208 0.516 -0.417 0.785 -0.622L7.549 6.732s-1.131 -1.649 0.519 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l7.947 11.589c0.292 -0.179 0.581 -0.334 0.871 -0.498L12.238 4.729s-1.131 -1.649 0.518 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l7.854 11.454 1.194 1.742c-4.948 3.394 -5.419 9.779 -2.592 13.902 0.565 0.825 1.39 0.26 1.39 0.26 -3.393 -4.949 -2.357 -10.51 2.592 -13.903L24.515 8.62s-0.545 -1.924 1.378 -2.47c1.924 -0.545 2.47 1.379 2.47 1.379l1.685 5.004c0.668 1.984 1.379 3.961 2.32 5.831 2.657 5.28 1.07 11.842 -3.94 15.279 -5.465 3.747 -12.936 2.354 -16.684 -3.11L2.695 17.336z"
    
    11
    +        android:fillColor="#FFDC5D" />
    
    12
    +    <path
    
    13
    +        android:pathData="M12 32.042C8 32.042 3.958 28 3.958 24c0 -0.553 -0.405 -1 -0.958 -1s-1.042 0.447 -1.042 1C1.958 30 6 34.042 12 34.042c0.553 0 1 -0.489 1 -1.042s-0.447 -0.958 -1 -0.958z"
    
    14
    +        android:fillColor="#5DADEC" />
    
    15
    +    <path
    
    16
    +        android:pathData="M7 34c-3 0 -5 -2 -5 -5 0 -0.553 -0.447 -1 -1 -1s-1 0.447 -1 1c0 4 3 7 7 7 0.553 0 1 -0.447 1 -1s-0.447 -1 -1 -1zM24 2c-0.552 0 -1 0.448 -1 1s0.448 1 1 1c4 0 8 3.589 8 8 0 0.552 0.448 1 1 1s1 -0.448 1 -1c0 -5.514 -4 -10 -10 -10z"
    
    17
    +        android:fillColor="#5DADEC" />
    
    18
    +    <path
    
    19
    +        android:pathData="M29 0.042c-0.552 0 -1 0.406 -1 0.958s0.448 1.042 1 1.042c3 0 4.958 2.225 4.958 4.958 0 0.552 0.489 1 1.042 1s0.958 -0.448 0.958 -1C35.958 3.163 33 0.042 29 0.042z"
    
    20
    +        android:fillColor="#5DADEC" />
    
    21
    +</vector>
    \ No newline at end of file

  • mobile/android/fenix/app/src/main/res/layout/fragment_home.xml
    ... ... @@ -134,6 +134,12 @@
    134 134
     
    
    135 135
         </com.google.android.material.appbar.AppBarLayout>
    
    136 136
     
    
    137
    +    <androidx.compose.ui.platform.ComposeView
    
    138
    +        android:id="@+id/campaignBox"
    
    139
    +        android:layout_width="match_parent"
    
    140
    +        android:layout_height="wrap_content"
    
    141
    +        android:layout_gravity="center"/>
    
    142
    +
    
    137 143
         <androidx.recyclerview.widget.RecyclerView
    
    138 144
             android:id="@+id/sessionControlRecyclerView"
    
    139 145
             android:layout_width="match_parent"
    

  • mobile/android/fenix/app/src/main/res/values/preference_keys.xml
    ... ... @@ -431,4 +431,5 @@
    431 431
         <string name="pref_key_tor_network_settings_bridges_enabled">pref_key_tor_network_settings_bridges_enabled</string>
    
    432 432
     
    
    433 433
         <string name="pref_key_spoof_english" translatable="false">pref_key_spoof_english</string>
    
    434
    +    <string name="pref_key_hide_campaign_2025_ux_survey" translatable="false">pref_key_hide_campaign_2025_ux_survey_test</string>
    
    434 435
     </resources>