Loading mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +14 −0 Original line number Diff line number Diff line Loading @@ -161,8 +161,10 @@ import org.mozilla.fenix.wallpapers.Wallpaper import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.tor.TorCampaignViewModel import org.mozilla.fenix.tor.TorHomePage import org.mozilla.fenix.tor.UrlQuickLoadViewModel import java.util.Locale @Suppress("TooManyFunctions", "LargeClass") class HomeFragment : Fragment(), UserInteractionHandler { Loading @@ -177,6 +179,7 @@ class HomeFragment : Fragment(), UserInteractionHandler { internal val binding get() = _binding!! private val snackbarBinding = ViewBoundFeatureWrapper<SnackbarBinding>() private val torCampaignViewModel: TorCampaignViewModel by activityViewModels() private val homeViewModel: HomeScreenViewModel by activityViewModels() private val urlQuickLoadViewModel: UrlQuickLoadViewModel by activityViewModels() Loading Loading @@ -974,6 +977,17 @@ class HomeFragment : Fragment(), UserInteractionHandler { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { TorHomePage( torCampaignViewModel.shouldInitiallyShowPromo, onClicked = { val baseUrl = "https://www.torproject.org/donate" val locale = Locale.getDefault().getLanguage() val donateUrl = "${baseUrl}/donate-${locale}-yec2025" (requireActivity() as HomeActivity).openToBrowserAndLoad( searchTermOrURL = donateUrl, newTab = true, from = BrowserDirection.FromHome, ) }, toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP ) } Loading mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignCompose.kt 0 → 100644 +274 −0 Original line number Diff line number Diff line package org.mozilla.fenix.tor import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R import java.text.SimpleDateFormat import java.util.Date private val alternateLayoutThreshHold = 500.dp @Composable fun CampaignBox(shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit) { BoxWithConstraints( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { val alternateLayout = this.maxWidth >= alternateLayoutThreshHold CampaignLayout( alternateLayout, maxWidth = this.maxWidth, shouldShowYec, onDonateButtonClicked = onDonateButtonClicked, ) } } @Composable private fun CampaignLayout( alternateLayout: Boolean, maxWidth: Dp, shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit, ) { Column( modifier = Modifier .padding(horizontal = 22.dp) .fillMaxWidth(getVariableWidth(maxWidth)) .wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, ) { PurpleBox(alternateLayout, shouldShowYec, onDonateButtonClicked = onDonateButtonClicked) Spacer(Modifier.size(8.dp)) Text( text = stringResource(R.string.no_donation_required_yec), style = TextStyle( fontSize = 12.5.sp, lineHeight = 18.75.sp, fontFamily = FontFamily.SansSerif, fontWeight = FontWeight(400), color = PhotonColors.LightGrey05, textAlign = TextAlign.Center, ), ) } } private fun getVariableWidth(width: Dp): Float = (alternateLayoutThreshHold / width).coerceIn(0.80f, 1.0f) @Composable private fun PurpleBox( alternateLayout: Boolean, shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit, ) { Box( modifier = Modifier.background( colorResource(R.color.yec_background), shape = RoundedCornerShape(8.dp), ), ) { Row( modifier = Modifier .fillMaxWidth() .wrapContentHeight(), horizontalArrangement = Arrangement.End, ) { ExitIcon(shouldShowYec) } DynamicCampaignContent(alternateLayout, onDonateButtonClicked = onDonateButtonClicked) } } @Composable private fun ExitIcon(shouldShowYec: MutableState<Boolean>) { IconButton( modifier = Modifier.padding(8.dp), onClick = { shouldShowYec.value = false }, ) { Icon( painter = painterResource(id = R.drawable.ic_close), tint = PhotonColors.White, contentDescription = stringResource(R.string.close_yec_button_description), ) } } @Composable private fun DynamicCampaignContent( alternateLayout: Boolean, onDonateButtonClicked: () -> Unit, ) { @Composable fun Icon(shouldShow: Boolean) { if (shouldShow) { Image( painterResource(R.drawable.globe_chain_burst_yec), contentDescription = null, alignment = Alignment.Center, ) } } Row( modifier = Modifier .padding(16.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { Column( modifier = Modifier.fillMaxWidth(if (alternateLayout) 0.75f else 1.0f), horizontalAlignment = if (alternateLayout) Alignment.Start else Alignment.CenterHorizontally, ) { Icon(shouldShow = !alternateLayout) TitleText(alternateLayout) MainText(onDonateButtonClicked) Row( horizontalArrangement = if (alternateLayout) Arrangement.Start else Arrangement.Center, modifier = Modifier.fillMaxWidth(), ) { DonateButton(onDonateButtonClicked = onDonateButtonClicked) } } Icon(shouldShow = alternateLayout) } } @Composable private fun TitleText(alternateLayout: Boolean) { Text( text = stringResource(R.string.free_the_internet_yec), style = TextStyle( fontSize = 56.sp, lineHeight = 47.6.sp, fontFamily = FontFamily(Font(R.font.jacquard_12)), fontWeight = FontWeight(400), color = PhotonColors.White, textAlign = if (alternateLayout) TextAlign.Start else TextAlign.Center, ), ) } @Composable private fun MainText(onDonateButtonClicked: () -> Unit) { Column { BasicText( text = buildAnnotatedString { withStyle( SpanStyle( color = PhotonColors.LightGrey05, fontSize = 14.sp, ), ) { append( stringResource( R.string.body1_yec, stringResource(R.string.body1_link_yec), ), ) val linkStart: Int = stringResource((R.string.body1_yec)).indexOf("%s") addLink( url = LinkAnnotation.Url( url = stringResource(R.string.body1_link_yec), styles = TextLinkStyles( style = SpanStyle(colorResource(R.color.yec_green)), pressedStyle = SpanStyle(PhotonColors.LightGrey05), ), linkInteractionListener = { onDonateButtonClicked() }, ), start = linkStart, end = linkStart + stringResource(R.string.body1_link_yec).length, ) } }, modifier = Modifier .fillMaxWidth() .padding(bottom = 8.dp), ) Text( text = stringResource(R.string.body2_yec), modifier = Modifier .fillMaxWidth() .padding( bottom = 16.dp, ), color = PhotonColors.LightGrey05, fontSize = 14.sp, textAlign = TextAlign.Start, ) } } @Composable private fun DonateButton(onDonateButtonClicked: () -> Unit) { Button( onClick = onDonateButtonClicked, colors = ButtonDefaults.buttonColors( backgroundColor = colorResource(R.color.yec_green), ), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 9.dp), ) { Text( text = stringResource(R.string.donate_now_yec), textAlign = TextAlign.Center, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, color = PhotonColors.DarkGrey80, ) Spacer( Modifier.size(4.dp), ) Image( painterResource(R.drawable.heart), contentDescription = null, ) } } mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorCampaignViewModel.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line package org.mozilla.fenix.tor import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import java.text.SimpleDateFormat import java.util.Date import kotlin.getValue import org.mozilla.geckoview.BuildConfig class TorCampaignViewModel : ViewModel() { val shouldInitiallyShowPromo: MutableState<Boolean> by lazy { mutableStateOf(shouldInitiallyShowPromo()) } fun shouldInitiallyShowPromo(): Boolean { // return true // uncomment to test val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz") val startDate = dateFormat.parse("2025-10-14-15-UTC") // from https://gitlab.torproject.org/tpo/web/team/-/issues/66 val endDate = dateFormat.parse("2026-01-02-00-UTC") // from https://gitlab.torproject.org/tpo/web/team/-/issues/66#note_3257224 val currentDate = Date() if (currentDate.before(startDate) || currentDate.after(endDate)) { return false } if (BuildConfig.BUILD_TYPE == "release") { return true } return false } } mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorHomePage.kt +35 −22 Original line number Diff line number Diff line Loading @@ -12,6 +12,9 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.paint Loading @@ -34,8 +37,13 @@ import org.mozilla.fenix.R @Composable @FlexibleWindowLightDarkPreview fun TorHomePage( shouldInitiallyShowPromo: MutableState<Boolean> = mutableStateOf(false), onClicked: () -> Unit = {}, toolBarAtTop: Boolean = true, ) { val shouldShowPromo = rememberSaveable { shouldInitiallyShowPromo } Column( modifier = Modifier .fillMaxSize() Loading Loading @@ -82,6 +90,10 @@ fun TorHomePage( ) } Spacer(Modifier.weight(1f)) if (shouldShowPromo.value) { CampaignBox(shouldShowPromo, onDonateButtonClicked = onClicked) Spacer(Modifier.weight(1f)) } else { Text( // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2 // "[android] Modify UI/UX", and the file HomeFragment. Loading Loading @@ -109,3 +121,4 @@ fun TorHomePage( } Spacer(modifier = Modifier.size(17.dp)) } } mobile/android/fenix/app/src/main/res/drawable/globe_chain_burst_yec.xml 0 → 100644 +35 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="124dp" android:viewportHeight="124" android:viewportWidth="160" android:width="160dp"> <path android:fillColor="#B6E368" android:pathData="M79.97,86.16C65.19,89.07 56.28,80.34 53.67,75.61L34.02,84.97L55.14,84.24L42.62,106.51L63.16,91.39L61.15,117.92L73.64,93L84.86,111.85L79.98,86.16H79.97Z"/> <path android:fillColor="#B6E368" android:pathData="M72.38,32.02C87.13,28.91 96.16,37.52 98.83,42.21L118.35,32.58L97.24,33.6L109.46,11.17L89.13,26.57L90.78,0L78.63,25.09L67.15,6.4L72.38,32.02Z"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:fillType="evenOdd" android:pathData="M82.86,42.77H84.19V44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09H82.79V48.34H81.4V45.59H80.06V44.18H78.74V42.84H77.34V53.77H84.19V51.09H85.59V53.77H89.58V55.18H92.31V56.59H89.51V55.18H85.6V67.44H89.58V66.1H92.38V67.44H93.71V68.85H92.38V70.26H91.05V71.67H89.65V73.01H88.25V74.35H86.99V75.76H85.6V74.35H86.86V72.94H88.25V71.6H89.65V70.26H90.98V68.85H92.31V67.51H89.58V68.85H85.6V71.67H84.2V74.42H82.8V75.76H85.6V77.17H81.4V74.35H82.8V71.6H84.19V68.85H77.34V77.17H81.4V78.43H71.88V77.17H67.76V75.76H66.43V74.35H65.1V73.01H63.7V71.67H62.38V70.26H63.78V71.6H65.1V72.94H66.5V74.35H67.83V75.76H70.56V74.42H69.16V71.67H67.83V68.85H63.7V67.51H60.98V68.85H59.58V67.44H58.25V55.18H59.65V67.44H60.98V66.1H63.78V67.44H67.83V55.18H63.78V56.59H60.98V55.18H59.65V52.36H60.98V49.61H62.38V48.27H63.78V46.93H65.1V45.59H66.43V44.18H69.23V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.83V51.09H69.22V53.77H75.94V42.84H74.68V44.18H73.28V42.84H70.62V44.18H69.22V42.77H70.55V41.43H82.86V42.77V42.77ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.94V55.17H69.22V67.44Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M62.37,70.25H60.98V68.84H62.37V70.25Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M95.1,67.44H93.7V55.17H95.1V67.44Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M86.99,45.58H88.25V46.92H89.64V48.26H90.97V49.6H92.37V52.35H93.7V55.17H92.3V52.42H90.97V49.67H89.58V48.33H88.25V46.99H86.85V45.58H84.19V44.17H86.99V45.58V45.58Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M73.28,45.58H71.95V48.33H70.62V51.08H69.22V48.26H70.55V45.51H71.88V44.17H73.28V45.58Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#00000000" android:pathData="M84.19,44.18V42.77H82.86V41.43H70.55V42.77H69.22V44.18M84.19,44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09M84.19,44.18V45.59H86.85V47H88.25V48.34H89.58V49.68H90.97V52.43H92.3V55.18M84.19,44.18H86.99V45.59H88.25V46.93H89.64V48.27H90.97V49.61H92.37V52.35H93.7V55.17M69.22,44.18H66.43V45.59H65.1V46.93H63.77V48.27H62.37V49.61H60.98V52.35H59.65V55.17M69.22,44.18V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.82V51.09H69.22M69.22,44.18H70.62V42.84H73.28V44.18M93.7,55.17H95.1V67.44H93.7M93.7,55.17V67.44M84.19,51.08H82.79V48.33H81.4V45.58H80.06V44.17H78.74V42.84H77.34V53.76H84.19V51.08H84.19ZM84.19,51.08H85.59V53.76H89.58V55.17H92.3M93.7,67.44H92.37V66.1H89.58V67.44H85.59V55.17H89.5V56.58H92.3V55.17H93.7M93.7,67.44V68.85H92.37V70.26H91.04V71.67H89.64V73.01H88.25V74.35H86.99V75.76H85.59M85.59,75.75V74.34H86.85V72.93H88.25V71.59H89.64V70.25H90.97V68.84H92.3V67.5H89.58V68.84H85.59V71.66H84.19V74.41H82.79V75.75H85.59V75.75ZM85.59,75.75V77.16H81.39M81.39,77.16V74.34H82.79V71.59H84.19V68.84H77.33V77.16H81.39H81.39ZM81.39,77.16V78.43H71.88V77.16H67.75V75.75H66.43V74.34H65.1V73H63.7V71.66H62.37V70.25M62.37,70.25H63.77V71.59H65.1V72.93H66.5V74.34H67.83V75.75H70.55V74.41H69.16V71.66H67.83V68.85H63.7V67.51H60.97V68.85M62.37,70.25H60.98V68.84H62.37V70.25ZM60.97,68.84H59.57V67.43H58.25V55.17H59.65M59.65,55.17V67.44H60.98V66.1H63.77V67.44H67.83V55.17H63.77V56.58H60.98V55.17H59.65ZM69.22,51.08V53.76H75.93V42.84H74.68V44.17H73.28M69.22,51.08H70.62V48.33H71.95V45.58H73.28V44.17H71.88V45.51H70.55V48.26H69.22V51.08ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.93V55.17H69.22V67.44Z" android:strokeAlpha="0.8" android:strokeColor="#C272FF" android:strokeWidth="0.23451"/> <path android:fillColor="#ffffff" android:pathData="M53.84,42.78H56.64L54.84,40.42L58.14,39.7L55.15,36.24L57.62,36.17L57.73,35.5L41.11,29.32C40.64,29.14 40.17,29 39.7,28.88L31.58,7.75C29.81,3.15 25.32,0.06 20.41,0.06C18.94,0.06 17.49,0.33 16.11,0.87L7.65,4.16C1.5,6.55 -1.57,13.54 0.81,19.73L12.4,49.92C14.17,54.53 18.66,57.62 23.56,57.62C24.22,57.62 24.87,57.57 25.51,57.46C26.74,58.58 28.17,59.46 29.77,60.05L46.5,66.27L44.54,62.76H48.38L47.08,59.56H51.23L49.53,56.39L38.61,52.33C42.24,49.92 44.27,45.7 43.94,41.38L56.68,46.11L53.84,42.78ZM49.04,56.96L50.07,58.86H46.04L47.35,62.07H43.36L44.97,64.96L30.01,59.4C26.89,58.24 24.41,55.93 23.03,52.89C21.64,49.86 21.52,46.48 22.67,43.35L24.89,37.34C25.95,34.46 27.98,32.2 30.46,30.8L33.78,39.46C33.55,39.74 33.36,40.07 33.23,40.44L31.01,46.45C30.68,47.35 30.72,48.31 31.11,49.17C31.5,50.04 32.21,50.7 33.1,51.03L49.04,56.96ZM21.23,46.86C21.17,46.76 21.12,46.65 21.08,46.54L9.48,16.35C9.13,15.45 9.36,14.68 9.53,14.31C9.7,13.93 10.11,13.24 11.01,12.89L19.46,9.6C19.78,9.48 20.1,9.42 20.41,9.42C21.32,9.42 22.44,9.95 22.9,11.14L30.21,30.15C27.54,31.61 25.37,34.02 24.24,37.1L22.02,43.11C21.57,44.33 21.3,45.6 21.23,46.86ZM33.88,40.68C33.93,40.52 34,40.38 34.08,40.24L34.5,41.32C34.85,42.23 34.62,42.99 34.45,43.37H34.45C34.28,43.75 33.88,44.43 32.98,44.78L32.26,45.06L33.88,40.68ZM37.79,52.02L33.34,50.37C32.62,50.11 32.06,49.58 31.74,48.88C31.42,48.19 31.39,47.41 31.66,46.7L31.94,45.93L33.23,45.43C34.36,45 34.87,44.13 35.08,43.65C35.29,43.17 35.58,42.21 35.14,41.07L34.58,39.61V39.59L34.35,39.01L34.32,38.91L31.07,30.48L31.06,30.46L30.85,29.91L30.82,29.82L23.55,10.88C22.97,9.39 21.56,8.72 20.41,8.72C20.01,8.72 19.61,8.8 19.21,8.95L10.76,12.24C9.63,12.68 9.11,13.55 8.9,14.02C8.69,14.5 8.4,15.47 8.84,16.61L20.43,46.8C20.61,47.27 20.88,47.67 21.2,47.97C21.24,49.75 21.64,51.52 22.4,53.18C23.03,54.56 23.87,55.79 24.88,56.85C24.44,56.9 24,56.93 23.56,56.93C18.94,56.93 14.71,54.01 13.04,49.67L1.45,19.48C-0.79,13.65 2.1,7.07 7.9,4.81L16.35,1.52C17.66,1.01 19.03,0.75 20.41,0.75C25.04,0.75 29.26,3.67 30.93,8.01L38.88,28.7L38.91,28.78L39.16,29.43L39.18,29.47L42.53,38.2C42.8,38.9 42.99,39.61 43.11,40.33C43.12,40.35 43.13,40.37 43.13,40.4C43.17,40.61 43.2,40.83 43.22,41.05C43.22,41.07 43.22,41.09 43.22,41.11C43.68,45.46 41.58,49.76 37.79,52.02ZM43.86,40.6C43.74,39.71 43.51,38.81 43.18,37.94L40.01,29.69C40.29,29.77 40.58,29.86 40.87,29.97L55.81,35.53L53.67,35.59L56.85,39.27L53.63,39.97L55.24,42.09H52.33L54.41,44.53L43.86,40.6Z"/> <path android:fillColor="#ffffff" android:pathData="M157.76,99.65L136.71,69.02C134.41,65.69 130.67,63.66 126.65,63.57C125.48,62.52 124.09,61.66 122.51,61.08L107.43,55.47L107.36,55.44L103.68,55.55L106.41,58.69L102.99,59.44L105.06,62.15H102.4L104.54,64.66L115.19,68.62L112.76,70.31C109.62,72.5 107.78,75.86 107.41,79.41L97.62,75.77L99.32,78.92H94.98L96.29,82.12H92.65L94.1,84.72L111.42,91.16C111.59,91.22 111.75,91.28 111.92,91.33L130.63,118.54C132.98,121.96 136.85,124 140.98,124C143.54,124 146.01,123.22 148.12,121.75L154.58,117.26C157.34,115.33 159.19,112.44 159.79,109.12C160.4,105.79 159.68,102.43 157.76,99.65ZM104.95,64.06L103.91,62.85H106.46L104.21,59.89L107.7,59.13L105.17,56.2L107.25,56.14L122.27,61.73C123.5,62.19 124.62,62.82 125.59,63.59C123.31,63.73 121.12,64.49 119.22,65.82L115.89,68.13L104.95,64.06ZM111.65,90.5L111.25,90.35L94.58,84.15L93.84,82.82H97.32L96.01,79.61H100.48L99.1,77.06L107.36,80.13L108.04,80.38L114.5,82.79C115.37,83.11 116.28,83.1 117.1,82.82C117.32,82.75 117.54,82.65 117.74,82.53C118.53,82.1 119.17,81.39 119.5,80.48L120.95,76.56L124.77,73.9C125.25,73.57 125.79,73.39 126.36,73.39C126.8,73.39 127.92,73.51 128.68,74.61L129.76,76.19C129.69,76.47 129.61,76.75 129.51,77.02L127.19,83.33L127.08,83.63C126.11,86.07 124.45,88.03 122.42,89.34C122.23,89.47 122.03,89.59 121.83,89.7C119.04,91.29 115.64,91.75 112.36,90.74C112.13,90.67 111.89,90.59 111.65,90.5ZM117.15,80.21C117.22,79.78 117.48,78.97 118.31,78.39L119.95,77.26L118.85,80.24C118.57,81 118.04,81.58 117.38,81.94C117.04,81.24 117.08,80.57 117.15,80.21ZM130.06,77.56L130.17,77.25C130.2,77.14 130.24,77.04 130.27,76.93L149.73,105.24C150.31,106.08 150.27,106.93 150.19,107.36C150.11,107.79 149.85,108.6 149.02,109.18L142.57,113.67C142.09,114.01 141.55,114.17 140.98,114.17C140.53,114.17 139.41,114.06 138.66,112.95L122.82,89.91C124.96,88.52 126.7,86.46 127.72,83.89L130.06,77.56ZM159.11,108.99C158.54,112.14 156.79,114.87 154.18,116.68L147.73,121.18C145.73,122.57 143.4,123.3 140.98,123.3C137.07,123.3 133.42,121.38 131.2,118.15L112.97,91.63C113.95,91.86 114.94,91.97 115.91,91.97C118.15,91.97 120.32,91.38 122.23,90.28L138.09,113.35C139.03,114.72 140.42,114.87 140.98,114.87C141.7,114.87 142.36,114.66 142.96,114.25L149.42,109.75C150.45,109.03 150.77,108.02 150.87,107.48C150.96,106.95 151.02,105.89 150.3,104.85L130.51,76.06L129.97,75.26L129.25,74.22C128.3,72.84 126.91,72.7 126.36,72.7H126.36C125.64,72.7 124.97,72.91 124.38,73.32L121.37,75.42L120.37,76.11L117.92,77.82C116.88,78.54 116.56,79.55 116.46,80.08C116.39,80.53 116.33,81.34 116.74,82.2C116.1,82.39 115.41,82.38 114.74,82.13L108.08,79.66C108.39,76.25 110.14,72.99 113.15,70.89L115.99,68.91L116.68,68.43L119.61,66.39C121.6,65 123.94,64.27 126.36,64.27H126.37C126.72,64.27 127.07,64.28 127.41,64.31C130.91,64.62 134.12,66.48 136.13,69.42L157.19,100.05C159,102.68 159.68,105.85 159.11,108.99Z"/> <path android:fillColor="#C272FF" android:pathData="M26.73,77.81L30.91,77.83L30.91,79.04L26.73,79.03V84.32H25.51V79.03L21.2,79.02L21.2,77.8L25.51,77.81V72.54H26.73V77.81Z"/> <path android:fillColor="#C272FF" android:pathData="M132.82,37.87L137,37.88L137,39.08L132.82,39.07V44.31H131.6V39.07L127.29,39.06L127.29,37.85L131.6,37.86V32.65H132.82V37.87Z"/> <path android:fillColor="#C272FF" android:pathData="M20.17,99.2C20.17,98.79 19.84,98.46 19.43,98.46C19.02,98.46 18.69,98.79 18.69,99.2C18.69,99.62 19.02,99.95 19.43,99.95V101.17C18.36,101.17 17.49,100.29 17.49,99.2C17.49,98.12 18.36,97.24 19.43,97.24C20.51,97.24 21.38,98.12 21.38,99.2C21.38,100.29 20.51,101.17 19.43,101.17V99.95C19.84,99.95 20.17,99.62 20.17,99.2Z"/> <path android:fillColor="#C272FF" android:pathData="M116.28,43.22C117.35,43.22 118.22,44.1 118.22,45.18C118.22,46.27 117.35,47.15 116.28,47.15C115.2,47.15 114.33,46.27 114.33,45.18C114.33,44.1 115.2,43.22 116.28,43.22Z"/> <path android:fillColor="#C272FF" android:pathData="M134.74,51.08C135.81,51.08 136.68,51.96 136.68,53.04C136.68,54.12 135.81,55 134.74,55C133.66,55 132.79,54.12 132.79,53.04C132.79,51.96 133.66,51.08 134.74,51.08Z"/> <path android:fillColor="#C272FF" android:pathData="M32.81,91.67C32.81,91.26 32.47,90.93 32.06,90.93C31.66,90.93 31.32,91.26 31.32,91.67C31.32,92.09 31.66,92.42 32.06,92.42V93.64C30.99,93.64 30.12,92.76 30.12,91.67C30.12,90.59 30.99,89.71 32.06,89.71C33.14,89.71 34.01,90.59 34.01,91.67C34.01,92.76 33.14,93.64 32.06,93.64V92.42C32.47,92.42 32.81,92.09 32.81,91.67Z"/> </vector> Loading
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +14 −0 Original line number Diff line number Diff line Loading @@ -161,8 +161,10 @@ import org.mozilla.fenix.wallpapers.Wallpaper import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.tor.TorCampaignViewModel import org.mozilla.fenix.tor.TorHomePage import org.mozilla.fenix.tor.UrlQuickLoadViewModel import java.util.Locale @Suppress("TooManyFunctions", "LargeClass") class HomeFragment : Fragment(), UserInteractionHandler { Loading @@ -177,6 +179,7 @@ class HomeFragment : Fragment(), UserInteractionHandler { internal val binding get() = _binding!! private val snackbarBinding = ViewBoundFeatureWrapper<SnackbarBinding>() private val torCampaignViewModel: TorCampaignViewModel by activityViewModels() private val homeViewModel: HomeScreenViewModel by activityViewModels() private val urlQuickLoadViewModel: UrlQuickLoadViewModel by activityViewModels() Loading Loading @@ -974,6 +977,17 @@ class HomeFragment : Fragment(), UserInteractionHandler { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { TorHomePage( torCampaignViewModel.shouldInitiallyShowPromo, onClicked = { val baseUrl = "https://www.torproject.org/donate" val locale = Locale.getDefault().getLanguage() val donateUrl = "${baseUrl}/donate-${locale}-yec2025" (requireActivity() as HomeActivity).openToBrowserAndLoad( searchTermOrURL = donateUrl, newTab = true, from = BrowserDirection.FromHome, ) }, toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP ) } Loading
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignCompose.kt 0 → 100644 +274 −0 Original line number Diff line number Diff line package org.mozilla.fenix.tor import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import mozilla.components.ui.colors.PhotonColors import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R import java.text.SimpleDateFormat import java.util.Date private val alternateLayoutThreshHold = 500.dp @Composable fun CampaignBox(shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit) { BoxWithConstraints( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { val alternateLayout = this.maxWidth >= alternateLayoutThreshHold CampaignLayout( alternateLayout, maxWidth = this.maxWidth, shouldShowYec, onDonateButtonClicked = onDonateButtonClicked, ) } } @Composable private fun CampaignLayout( alternateLayout: Boolean, maxWidth: Dp, shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit, ) { Column( modifier = Modifier .padding(horizontal = 22.dp) .fillMaxWidth(getVariableWidth(maxWidth)) .wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, ) { PurpleBox(alternateLayout, shouldShowYec, onDonateButtonClicked = onDonateButtonClicked) Spacer(Modifier.size(8.dp)) Text( text = stringResource(R.string.no_donation_required_yec), style = TextStyle( fontSize = 12.5.sp, lineHeight = 18.75.sp, fontFamily = FontFamily.SansSerif, fontWeight = FontWeight(400), color = PhotonColors.LightGrey05, textAlign = TextAlign.Center, ), ) } } private fun getVariableWidth(width: Dp): Float = (alternateLayoutThreshHold / width).coerceIn(0.80f, 1.0f) @Composable private fun PurpleBox( alternateLayout: Boolean, shouldShowYec: MutableState<Boolean>, onDonateButtonClicked: () -> Unit, ) { Box( modifier = Modifier.background( colorResource(R.color.yec_background), shape = RoundedCornerShape(8.dp), ), ) { Row( modifier = Modifier .fillMaxWidth() .wrapContentHeight(), horizontalArrangement = Arrangement.End, ) { ExitIcon(shouldShowYec) } DynamicCampaignContent(alternateLayout, onDonateButtonClicked = onDonateButtonClicked) } } @Composable private fun ExitIcon(shouldShowYec: MutableState<Boolean>) { IconButton( modifier = Modifier.padding(8.dp), onClick = { shouldShowYec.value = false }, ) { Icon( painter = painterResource(id = R.drawable.ic_close), tint = PhotonColors.White, contentDescription = stringResource(R.string.close_yec_button_description), ) } } @Composable private fun DynamicCampaignContent( alternateLayout: Boolean, onDonateButtonClicked: () -> Unit, ) { @Composable fun Icon(shouldShow: Boolean) { if (shouldShow) { Image( painterResource(R.drawable.globe_chain_burst_yec), contentDescription = null, alignment = Alignment.Center, ) } } Row( modifier = Modifier .padding(16.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { Column( modifier = Modifier.fillMaxWidth(if (alternateLayout) 0.75f else 1.0f), horizontalAlignment = if (alternateLayout) Alignment.Start else Alignment.CenterHorizontally, ) { Icon(shouldShow = !alternateLayout) TitleText(alternateLayout) MainText(onDonateButtonClicked) Row( horizontalArrangement = if (alternateLayout) Arrangement.Start else Arrangement.Center, modifier = Modifier.fillMaxWidth(), ) { DonateButton(onDonateButtonClicked = onDonateButtonClicked) } } Icon(shouldShow = alternateLayout) } } @Composable private fun TitleText(alternateLayout: Boolean) { Text( text = stringResource(R.string.free_the_internet_yec), style = TextStyle( fontSize = 56.sp, lineHeight = 47.6.sp, fontFamily = FontFamily(Font(R.font.jacquard_12)), fontWeight = FontWeight(400), color = PhotonColors.White, textAlign = if (alternateLayout) TextAlign.Start else TextAlign.Center, ), ) } @Composable private fun MainText(onDonateButtonClicked: () -> Unit) { Column { BasicText( text = buildAnnotatedString { withStyle( SpanStyle( color = PhotonColors.LightGrey05, fontSize = 14.sp, ), ) { append( stringResource( R.string.body1_yec, stringResource(R.string.body1_link_yec), ), ) val linkStart: Int = stringResource((R.string.body1_yec)).indexOf("%s") addLink( url = LinkAnnotation.Url( url = stringResource(R.string.body1_link_yec), styles = TextLinkStyles( style = SpanStyle(colorResource(R.color.yec_green)), pressedStyle = SpanStyle(PhotonColors.LightGrey05), ), linkInteractionListener = { onDonateButtonClicked() }, ), start = linkStart, end = linkStart + stringResource(R.string.body1_link_yec).length, ) } }, modifier = Modifier .fillMaxWidth() .padding(bottom = 8.dp), ) Text( text = stringResource(R.string.body2_yec), modifier = Modifier .fillMaxWidth() .padding( bottom = 16.dp, ), color = PhotonColors.LightGrey05, fontSize = 14.sp, textAlign = TextAlign.Start, ) } } @Composable private fun DonateButton(onDonateButtonClicked: () -> Unit) { Button( onClick = onDonateButtonClicked, colors = ButtonDefaults.buttonColors( backgroundColor = colorResource(R.color.yec_green), ), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 9.dp), ) { Text( text = stringResource(R.string.donate_now_yec), textAlign = TextAlign.Center, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, color = PhotonColors.DarkGrey80, ) Spacer( Modifier.size(4.dp), ) Image( painterResource(R.drawable.heart), contentDescription = null, ) } }
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorCampaignViewModel.kt 0 → 100644 +34 −0 Original line number Diff line number Diff line package org.mozilla.fenix.tor import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import java.text.SimpleDateFormat import java.util.Date import kotlin.getValue import org.mozilla.geckoview.BuildConfig class TorCampaignViewModel : ViewModel() { val shouldInitiallyShowPromo: MutableState<Boolean> by lazy { mutableStateOf(shouldInitiallyShowPromo()) } fun shouldInitiallyShowPromo(): Boolean { // return true // uncomment to test val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz") val startDate = dateFormat.parse("2025-10-14-15-UTC") // from https://gitlab.torproject.org/tpo/web/team/-/issues/66 val endDate = dateFormat.parse("2026-01-02-00-UTC") // from https://gitlab.torproject.org/tpo/web/team/-/issues/66#note_3257224 val currentDate = Date() if (currentDate.before(startDate) || currentDate.after(endDate)) { return false } if (BuildConfig.BUILD_TYPE == "release") { return true } return false } }
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorHomePage.kt +35 −22 Original line number Diff line number Diff line Loading @@ -12,6 +12,9 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.paint Loading @@ -34,8 +37,13 @@ import org.mozilla.fenix.R @Composable @FlexibleWindowLightDarkPreview fun TorHomePage( shouldInitiallyShowPromo: MutableState<Boolean> = mutableStateOf(false), onClicked: () -> Unit = {}, toolBarAtTop: Boolean = true, ) { val shouldShowPromo = rememberSaveable { shouldInitiallyShowPromo } Column( modifier = Modifier .fillMaxSize() Loading Loading @@ -82,6 +90,10 @@ fun TorHomePage( ) } Spacer(Modifier.weight(1f)) if (shouldShowPromo.value) { CampaignBox(shouldShowPromo, onDonateButtonClicked = onClicked) Spacer(Modifier.weight(1f)) } else { Text( // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2 // "[android] Modify UI/UX", and the file HomeFragment. Loading Loading @@ -109,3 +121,4 @@ fun TorHomePage( } Spacer(modifier = Modifier.size(17.dp)) } }
mobile/android/fenix/app/src/main/res/drawable/globe_chain_burst_yec.xml 0 → 100644 +35 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="124dp" android:viewportHeight="124" android:viewportWidth="160" android:width="160dp"> <path android:fillColor="#B6E368" android:pathData="M79.97,86.16C65.19,89.07 56.28,80.34 53.67,75.61L34.02,84.97L55.14,84.24L42.62,106.51L63.16,91.39L61.15,117.92L73.64,93L84.86,111.85L79.98,86.16H79.97Z"/> <path android:fillColor="#B6E368" android:pathData="M72.38,32.02C87.13,28.91 96.16,37.52 98.83,42.21L118.35,32.58L97.24,33.6L109.46,11.17L89.13,26.57L90.78,0L78.63,25.09L67.15,6.4L72.38,32.02Z"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:fillType="evenOdd" android:pathData="M82.86,42.77H84.19V44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09H82.79V48.34H81.4V45.59H80.06V44.18H78.74V42.84H77.34V53.77H84.19V51.09H85.59V53.77H89.58V55.18H92.31V56.59H89.51V55.18H85.6V67.44H89.58V66.1H92.38V67.44H93.71V68.85H92.38V70.26H91.05V71.67H89.65V73.01H88.25V74.35H86.99V75.76H85.6V74.35H86.86V72.94H88.25V71.6H89.65V70.26H90.98V68.85H92.31V67.51H89.58V68.85H85.6V71.67H84.2V74.42H82.8V75.76H85.6V77.17H81.4V74.35H82.8V71.6H84.19V68.85H77.34V77.17H81.4V78.43H71.88V77.17H67.76V75.76H66.43V74.35H65.1V73.01H63.7V71.67H62.38V70.26H63.78V71.6H65.1V72.94H66.5V74.35H67.83V75.76H70.56V74.42H69.16V71.67H67.83V68.85H63.7V67.51H60.98V68.85H59.58V67.44H58.25V55.18H59.65V67.44H60.98V66.1H63.78V67.44H67.83V55.18H63.78V56.59H60.98V55.18H59.65V52.36H60.98V49.61H62.38V48.27H63.78V46.93H65.1V45.59H66.43V44.18H69.23V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.83V51.09H69.22V53.77H75.94V42.84H74.68V44.18H73.28V42.84H70.62V44.18H69.22V42.77H70.55V41.43H82.86V42.77V42.77ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.94V55.17H69.22V67.44Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M62.37,70.25H60.98V68.84H62.37V70.25Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M95.1,67.44H93.7V55.17H95.1V67.44Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M86.99,45.58H88.25V46.92H89.64V48.26H90.97V49.6H92.37V52.35H93.7V55.17H92.3V52.42H90.97V49.67H89.58V48.33H88.25V46.99H86.85V45.58H84.19V44.17H86.99V45.58V45.58Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M73.28,45.58H71.95V48.33H70.62V51.08H69.22V48.26H70.55V45.51H71.88V44.17H73.28V45.58Z" android:strokeAlpha="0.8"/> <path android:fillAlpha="0.8" android:fillColor="#00000000" android:pathData="M84.19,44.18V42.77H82.86V41.43H70.55V42.77H69.22V44.18M84.19,44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09M84.19,44.18V45.59H86.85V47H88.25V48.34H89.58V49.68H90.97V52.43H92.3V55.18M84.19,44.18H86.99V45.59H88.25V46.93H89.64V48.27H90.97V49.61H92.37V52.35H93.7V55.17M69.22,44.18H66.43V45.59H65.1V46.93H63.77V48.27H62.37V49.61H60.98V52.35H59.65V55.17M69.22,44.18V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.82V51.09H69.22M69.22,44.18H70.62V42.84H73.28V44.18M93.7,55.17H95.1V67.44H93.7M93.7,55.17V67.44M84.19,51.08H82.79V48.33H81.4V45.58H80.06V44.17H78.74V42.84H77.34V53.76H84.19V51.08H84.19ZM84.19,51.08H85.59V53.76H89.58V55.17H92.3M93.7,67.44H92.37V66.1H89.58V67.44H85.59V55.17H89.5V56.58H92.3V55.17H93.7M93.7,67.44V68.85H92.37V70.26H91.04V71.67H89.64V73.01H88.25V74.35H86.99V75.76H85.59M85.59,75.75V74.34H86.85V72.93H88.25V71.59H89.64V70.25H90.97V68.84H92.3V67.5H89.58V68.84H85.59V71.66H84.19V74.41H82.79V75.75H85.59V75.75ZM85.59,75.75V77.16H81.39M81.39,77.16V74.34H82.79V71.59H84.19V68.84H77.33V77.16H81.39H81.39ZM81.39,77.16V78.43H71.88V77.16H67.75V75.75H66.43V74.34H65.1V73H63.7V71.66H62.37V70.25M62.37,70.25H63.77V71.59H65.1V72.93H66.5V74.34H67.83V75.75H70.55V74.41H69.16V71.66H67.83V68.85H63.7V67.51H60.97V68.85M62.37,70.25H60.98V68.84H62.37V70.25ZM60.97,68.84H59.57V67.43H58.25V55.17H59.65M59.65,55.17V67.44H60.98V66.1H63.77V67.44H67.83V55.17H63.77V56.58H60.98V55.17H59.65ZM69.22,51.08V53.76H75.93V42.84H74.68V44.17H73.28M69.22,51.08H70.62V48.33H71.95V45.58H73.28V44.17H71.88V45.51H70.55V48.26H69.22V51.08ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.93V55.17H69.22V67.44Z" android:strokeAlpha="0.8" android:strokeColor="#C272FF" android:strokeWidth="0.23451"/> <path android:fillColor="#ffffff" android:pathData="M53.84,42.78H56.64L54.84,40.42L58.14,39.7L55.15,36.24L57.62,36.17L57.73,35.5L41.11,29.32C40.64,29.14 40.17,29 39.7,28.88L31.58,7.75C29.81,3.15 25.32,0.06 20.41,0.06C18.94,0.06 17.49,0.33 16.11,0.87L7.65,4.16C1.5,6.55 -1.57,13.54 0.81,19.73L12.4,49.92C14.17,54.53 18.66,57.62 23.56,57.62C24.22,57.62 24.87,57.57 25.51,57.46C26.74,58.58 28.17,59.46 29.77,60.05L46.5,66.27L44.54,62.76H48.38L47.08,59.56H51.23L49.53,56.39L38.61,52.33C42.24,49.92 44.27,45.7 43.94,41.38L56.68,46.11L53.84,42.78ZM49.04,56.96L50.07,58.86H46.04L47.35,62.07H43.36L44.97,64.96L30.01,59.4C26.89,58.24 24.41,55.93 23.03,52.89C21.64,49.86 21.52,46.48 22.67,43.35L24.89,37.34C25.95,34.46 27.98,32.2 30.46,30.8L33.78,39.46C33.55,39.74 33.36,40.07 33.23,40.44L31.01,46.45C30.68,47.35 30.72,48.31 31.11,49.17C31.5,50.04 32.21,50.7 33.1,51.03L49.04,56.96ZM21.23,46.86C21.17,46.76 21.12,46.65 21.08,46.54L9.48,16.35C9.13,15.45 9.36,14.68 9.53,14.31C9.7,13.93 10.11,13.24 11.01,12.89L19.46,9.6C19.78,9.48 20.1,9.42 20.41,9.42C21.32,9.42 22.44,9.95 22.9,11.14L30.21,30.15C27.54,31.61 25.37,34.02 24.24,37.1L22.02,43.11C21.57,44.33 21.3,45.6 21.23,46.86ZM33.88,40.68C33.93,40.52 34,40.38 34.08,40.24L34.5,41.32C34.85,42.23 34.62,42.99 34.45,43.37H34.45C34.28,43.75 33.88,44.43 32.98,44.78L32.26,45.06L33.88,40.68ZM37.79,52.02L33.34,50.37C32.62,50.11 32.06,49.58 31.74,48.88C31.42,48.19 31.39,47.41 31.66,46.7L31.94,45.93L33.23,45.43C34.36,45 34.87,44.13 35.08,43.65C35.29,43.17 35.58,42.21 35.14,41.07L34.58,39.61V39.59L34.35,39.01L34.32,38.91L31.07,30.48L31.06,30.46L30.85,29.91L30.82,29.82L23.55,10.88C22.97,9.39 21.56,8.72 20.41,8.72C20.01,8.72 19.61,8.8 19.21,8.95L10.76,12.24C9.63,12.68 9.11,13.55 8.9,14.02C8.69,14.5 8.4,15.47 8.84,16.61L20.43,46.8C20.61,47.27 20.88,47.67 21.2,47.97C21.24,49.75 21.64,51.52 22.4,53.18C23.03,54.56 23.87,55.79 24.88,56.85C24.44,56.9 24,56.93 23.56,56.93C18.94,56.93 14.71,54.01 13.04,49.67L1.45,19.48C-0.79,13.65 2.1,7.07 7.9,4.81L16.35,1.52C17.66,1.01 19.03,0.75 20.41,0.75C25.04,0.75 29.26,3.67 30.93,8.01L38.88,28.7L38.91,28.78L39.16,29.43L39.18,29.47L42.53,38.2C42.8,38.9 42.99,39.61 43.11,40.33C43.12,40.35 43.13,40.37 43.13,40.4C43.17,40.61 43.2,40.83 43.22,41.05C43.22,41.07 43.22,41.09 43.22,41.11C43.68,45.46 41.58,49.76 37.79,52.02ZM43.86,40.6C43.74,39.71 43.51,38.81 43.18,37.94L40.01,29.69C40.29,29.77 40.58,29.86 40.87,29.97L55.81,35.53L53.67,35.59L56.85,39.27L53.63,39.97L55.24,42.09H52.33L54.41,44.53L43.86,40.6Z"/> <path android:fillColor="#ffffff" android:pathData="M157.76,99.65L136.71,69.02C134.41,65.69 130.67,63.66 126.65,63.57C125.48,62.52 124.09,61.66 122.51,61.08L107.43,55.47L107.36,55.44L103.68,55.55L106.41,58.69L102.99,59.44L105.06,62.15H102.4L104.54,64.66L115.19,68.62L112.76,70.31C109.62,72.5 107.78,75.86 107.41,79.41L97.62,75.77L99.32,78.92H94.98L96.29,82.12H92.65L94.1,84.72L111.42,91.16C111.59,91.22 111.75,91.28 111.92,91.33L130.63,118.54C132.98,121.96 136.85,124 140.98,124C143.54,124 146.01,123.22 148.12,121.75L154.58,117.26C157.34,115.33 159.19,112.44 159.79,109.12C160.4,105.79 159.68,102.43 157.76,99.65ZM104.95,64.06L103.91,62.85H106.46L104.21,59.89L107.7,59.13L105.17,56.2L107.25,56.14L122.27,61.73C123.5,62.19 124.62,62.82 125.59,63.59C123.31,63.73 121.12,64.49 119.22,65.82L115.89,68.13L104.95,64.06ZM111.65,90.5L111.25,90.35L94.58,84.15L93.84,82.82H97.32L96.01,79.61H100.48L99.1,77.06L107.36,80.13L108.04,80.38L114.5,82.79C115.37,83.11 116.28,83.1 117.1,82.82C117.32,82.75 117.54,82.65 117.74,82.53C118.53,82.1 119.17,81.39 119.5,80.48L120.95,76.56L124.77,73.9C125.25,73.57 125.79,73.39 126.36,73.39C126.8,73.39 127.92,73.51 128.68,74.61L129.76,76.19C129.69,76.47 129.61,76.75 129.51,77.02L127.19,83.33L127.08,83.63C126.11,86.07 124.45,88.03 122.42,89.34C122.23,89.47 122.03,89.59 121.83,89.7C119.04,91.29 115.64,91.75 112.36,90.74C112.13,90.67 111.89,90.59 111.65,90.5ZM117.15,80.21C117.22,79.78 117.48,78.97 118.31,78.39L119.95,77.26L118.85,80.24C118.57,81 118.04,81.58 117.38,81.94C117.04,81.24 117.08,80.57 117.15,80.21ZM130.06,77.56L130.17,77.25C130.2,77.14 130.24,77.04 130.27,76.93L149.73,105.24C150.31,106.08 150.27,106.93 150.19,107.36C150.11,107.79 149.85,108.6 149.02,109.18L142.57,113.67C142.09,114.01 141.55,114.17 140.98,114.17C140.53,114.17 139.41,114.06 138.66,112.95L122.82,89.91C124.96,88.52 126.7,86.46 127.72,83.89L130.06,77.56ZM159.11,108.99C158.54,112.14 156.79,114.87 154.18,116.68L147.73,121.18C145.73,122.57 143.4,123.3 140.98,123.3C137.07,123.3 133.42,121.38 131.2,118.15L112.97,91.63C113.95,91.86 114.94,91.97 115.91,91.97C118.15,91.97 120.32,91.38 122.23,90.28L138.09,113.35C139.03,114.72 140.42,114.87 140.98,114.87C141.7,114.87 142.36,114.66 142.96,114.25L149.42,109.75C150.45,109.03 150.77,108.02 150.87,107.48C150.96,106.95 151.02,105.89 150.3,104.85L130.51,76.06L129.97,75.26L129.25,74.22C128.3,72.84 126.91,72.7 126.36,72.7H126.36C125.64,72.7 124.97,72.91 124.38,73.32L121.37,75.42L120.37,76.11L117.92,77.82C116.88,78.54 116.56,79.55 116.46,80.08C116.39,80.53 116.33,81.34 116.74,82.2C116.1,82.39 115.41,82.38 114.74,82.13L108.08,79.66C108.39,76.25 110.14,72.99 113.15,70.89L115.99,68.91L116.68,68.43L119.61,66.39C121.6,65 123.94,64.27 126.36,64.27H126.37C126.72,64.27 127.07,64.28 127.41,64.31C130.91,64.62 134.12,66.48 136.13,69.42L157.19,100.05C159,102.68 159.68,105.85 159.11,108.99Z"/> <path android:fillColor="#C272FF" android:pathData="M26.73,77.81L30.91,77.83L30.91,79.04L26.73,79.03V84.32H25.51V79.03L21.2,79.02L21.2,77.8L25.51,77.81V72.54H26.73V77.81Z"/> <path android:fillColor="#C272FF" android:pathData="M132.82,37.87L137,37.88L137,39.08L132.82,39.07V44.31H131.6V39.07L127.29,39.06L127.29,37.85L131.6,37.86V32.65H132.82V37.87Z"/> <path android:fillColor="#C272FF" android:pathData="M20.17,99.2C20.17,98.79 19.84,98.46 19.43,98.46C19.02,98.46 18.69,98.79 18.69,99.2C18.69,99.62 19.02,99.95 19.43,99.95V101.17C18.36,101.17 17.49,100.29 17.49,99.2C17.49,98.12 18.36,97.24 19.43,97.24C20.51,97.24 21.38,98.12 21.38,99.2C21.38,100.29 20.51,101.17 19.43,101.17V99.95C19.84,99.95 20.17,99.62 20.17,99.2Z"/> <path android:fillColor="#C272FF" android:pathData="M116.28,43.22C117.35,43.22 118.22,44.1 118.22,45.18C118.22,46.27 117.35,47.15 116.28,47.15C115.2,47.15 114.33,46.27 114.33,45.18C114.33,44.1 115.2,43.22 116.28,43.22Z"/> <path android:fillColor="#C272FF" android:pathData="M134.74,51.08C135.81,51.08 136.68,51.96 136.68,53.04C136.68,54.12 135.81,55 134.74,55C133.66,55 132.79,54.12 132.79,53.04C132.79,51.96 133.66,51.08 134.74,51.08Z"/> <path android:fillColor="#C272FF" android:pathData="M32.81,91.67C32.81,91.26 32.47,90.93 32.06,90.93C31.66,90.93 31.32,91.26 31.32,91.67C31.32,92.09 31.66,92.42 32.06,92.42V93.64C30.99,93.64 30.12,92.76 30.12,91.67C30.12,90.59 30.99,89.71 32.06,89.71C33.14,89.71 34.01,90.59 34.01,91.67C34.01,92.76 33.14,93.64 32.06,93.64V92.42C32.47,92.42 32.81,92.09 32.81,91.67Z"/> </vector>