clairehurst pushed to branch tor-browser-140.10.1esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
259110bb
by clairehurst at 2026-04-27T16:23:56-06:00
-
927330f8
by clairehurst at 2026-04-28T12:53:04-06:00
8 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/SearchBar.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignCompose.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorCampaignViewModel.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorHomePage.kt
- + mobile/android/fenix/app/src/main/res/drawable/globe_chain_burst_yec.xml
- + mobile/android/fenix/app/src/main/res/drawable/heart.xml
- mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
Changes:
| ... | ... | @@ -161,6 +161,7 @@ import org.mozilla.fenix.wallpapers.Wallpaper |
| 161 | 161 | import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics
|
| 162 | 162 | |
| 163 | 163 | import org.mozilla.fenix.components.toolbar.ToolbarPosition
|
| 164 | +import org.mozilla.fenix.tor.TorCampaignViewModel
|
|
| 164 | 165 | import org.mozilla.fenix.tor.TorHomePage
|
| 165 | 166 | import org.mozilla.fenix.tor.UrlQuickLoadViewModel
|
| 166 | 167 | |
| ... | ... | @@ -179,6 +180,7 @@ class HomeFragment : Fragment(), UserInteractionHandler { |
| 179 | 180 | |
| 180 | 181 | private val homeViewModel: HomeScreenViewModel by activityViewModels()
|
| 181 | 182 | private val urlQuickLoadViewModel: UrlQuickLoadViewModel by activityViewModels()
|
| 183 | + private val torCampaignViewModel: TorCampaignViewModel by activityViewModels()
|
|
| 182 | 184 | |
| 183 | 185 | private var _bottomToolbarContainerView: BottomToolbarContainerView? = null
|
| 184 | 186 | private val bottomToolbarContainerView: BottomToolbarContainerView
|
| ... | ... | @@ -974,7 +976,16 @@ class HomeFragment : Fragment(), UserInteractionHandler { |
| 974 | 976 | setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
| 975 | 977 | setContent {
|
| 976 | 978 | TorHomePage(
|
| 977 | - toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP
|
|
| 979 | + shouldInitiallyShowPromo = torCampaignViewModel.shouldInitiallyShowPromo,
|
|
| 980 | + toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP,
|
|
| 981 | + toolPair = torCampaignViewModel.getToolPair(),
|
|
| 982 | + onClicked = {
|
|
| 983 | + (requireActivity() as HomeActivity).openToBrowserAndLoad(
|
|
| 984 | + searchTermOrURL = "https://internetfreedom.torproject.org",
|
|
| 985 | + newTab = true,
|
|
| 986 | + from = BrowserDirection.FromHome,
|
|
| 987 | + )
|
|
| 988 | + }
|
|
| 978 | 989 | )
|
| 979 | 990 | }
|
| 980 | 991 | }
|
| ... | ... | @@ -34,7 +34,7 @@ internal fun SearchBar( |
| 34 | 34 | |
| 35 | 35 | @Composable
|
| 36 | 36 | @PreviewLightDark
|
| 37 | -private fun SearchBarPreview() {
|
|
| 37 | +fun SearchBarPreview() {
|
|
| 38 | 38 | FirefoxTheme {
|
| 39 | 39 | Column(
|
| 40 | 40 | modifier = Modifier.background(color = FirefoxTheme.colors.layer1),
|
| 1 | +package org.mozilla.fenix.tor
|
|
| 2 | + |
|
| 3 | +import androidx.compose.foundation.Image
|
|
| 4 | +import androidx.compose.foundation.background
|
|
| 5 | +import androidx.compose.foundation.layout.Arrangement
|
|
| 6 | +import androidx.compose.foundation.layout.Box
|
|
| 7 | +import androidx.compose.foundation.layout.BoxWithConstraints
|
|
| 8 | +import androidx.compose.foundation.layout.Column
|
|
| 9 | +import androidx.compose.foundation.layout.PaddingValues
|
|
| 10 | +import androidx.compose.foundation.layout.Row
|
|
| 11 | +import androidx.compose.foundation.layout.Spacer
|
|
| 12 | +import androidx.compose.foundation.layout.fillMaxHeight
|
|
| 13 | +import androidx.compose.foundation.layout.fillMaxWidth
|
|
| 14 | +import androidx.compose.foundation.layout.padding
|
|
| 15 | +import androidx.compose.foundation.layout.size
|
|
| 16 | +import androidx.compose.foundation.layout.wrapContentHeight
|
|
| 17 | +import androidx.compose.foundation.layout.wrapContentWidth
|
|
| 18 | +import androidx.compose.foundation.shape.RoundedCornerShape
|
|
| 19 | +import androidx.compose.material.Button
|
|
| 20 | +import androidx.compose.material.ButtonDefaults
|
|
| 21 | +import androidx.compose.material.Icon
|
|
| 22 | +import androidx.compose.material.IconButton
|
|
| 23 | +import androidx.compose.material.Text
|
|
| 24 | +import androidx.compose.runtime.Composable
|
|
| 25 | +import androidx.compose.runtime.MutableState
|
|
| 26 | +import androidx.compose.runtime.mutableStateOf
|
|
| 27 | +import androidx.compose.ui.Alignment
|
|
| 28 | +import androidx.compose.ui.Modifier
|
|
| 29 | +import androidx.compose.ui.res.colorResource
|
|
| 30 | +import androidx.compose.ui.res.painterResource
|
|
| 31 | +import androidx.compose.ui.res.stringResource
|
|
| 32 | +import androidx.compose.ui.text.AnnotatedString
|
|
| 33 | +import androidx.compose.ui.text.TextStyle
|
|
| 34 | +import androidx.compose.ui.text.font.FontFamily
|
|
| 35 | +import androidx.compose.ui.text.font.FontWeight
|
|
| 36 | +import androidx.compose.ui.text.fromHtml
|
|
| 37 | +import androidx.compose.ui.text.style.TextAlign
|
|
| 38 | +import androidx.compose.ui.tooling.preview.Preview
|
|
| 39 | +import androidx.compose.ui.unit.Dp
|
|
| 40 | +import androidx.compose.ui.unit.dp
|
|
| 41 | +import androidx.compose.ui.unit.sp
|
|
| 42 | +import mozilla.components.ui.colors.PhotonColors
|
|
| 43 | +import org.mozilla.fenix.R
|
|
| 44 | + |
|
| 45 | + |
|
| 46 | +private val alternateLayoutThreshHold = 500.dp
|
|
| 47 | + |
|
| 48 | +@Composable
|
|
| 49 | +@CampaignComposePreview
|
|
| 50 | +fun CampaignBox(
|
|
| 51 | + shouldShowPromo: MutableState<Boolean> = mutableStateOf(true),
|
|
| 52 | + onDonateButtonClicked: () -> Unit = {},
|
|
| 53 | + toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool> = Pair(TorCampaignViewModel.toolList[0], TorCampaignViewModel.toolList[1]),
|
|
| 54 | + ) {
|
|
| 55 | + BoxWithConstraints(
|
|
| 56 | + contentAlignment = Alignment.Center,
|
|
| 57 | + modifier = Modifier
|
|
| 58 | + .fillMaxWidth()
|
|
| 59 | + .fillMaxHeight(),
|
|
| 60 | + ) {
|
|
| 61 | + val alternateLayout = this.maxWidth >= alternateLayoutThreshHold
|
|
| 62 | + |
|
| 63 | + CampaignLayout(
|
|
| 64 | + alternateLayout,
|
|
| 65 | + maxWidth = this.maxWidth,
|
|
| 66 | + shouldShowPromo,
|
|
| 67 | + onDonateButtonClicked = onDonateButtonClicked,
|
|
| 68 | + toolPair = toolPair,
|
|
| 69 | + )
|
|
| 70 | + }
|
|
| 71 | +}
|
|
| 72 | + |
|
| 73 | +@Composable
|
|
| 74 | +private fun CampaignLayout(
|
|
| 75 | + alternateLayout: Boolean,
|
|
| 76 | + maxWidth: Dp,
|
|
| 77 | + shouldShowPromo: MutableState<Boolean>,
|
|
| 78 | + onDonateButtonClicked: () -> Unit,
|
|
| 79 | + toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
|
|
| 80 | +) {
|
|
| 81 | + Column(
|
|
| 82 | + modifier = Modifier
|
|
| 83 | + .padding(horizontal = 22.dp)
|
|
| 84 | + .fillMaxWidth(getVariableWidth(maxWidth))
|
|
| 85 | + .wrapContentHeight(),
|
|
| 86 | + horizontalAlignment = Alignment.CenterHorizontally,
|
|
| 87 | + ) {
|
|
| 88 | + PurpleBox(
|
|
| 89 | + alternateLayout,
|
|
| 90 | + shouldShowPromo,
|
|
| 91 | + onDonateButtonClicked = onDonateButtonClicked,
|
|
| 92 | + toolPair = toolPair,
|
|
| 93 | + )
|
|
| 94 | + Spacer(Modifier.size(8.dp))
|
|
| 95 | + Text(
|
|
| 96 | + text = stringResource(R.string.no_donation_required_yec),
|
|
| 97 | + style = TextStyle(
|
|
| 98 | + fontSize = 12.5.sp,
|
|
| 99 | + lineHeight = 18.75.sp,
|
|
| 100 | + fontFamily = FontFamily.SansSerif,
|
|
| 101 | + fontWeight = FontWeight(400),
|
|
| 102 | + color = PhotonColors.LightGrey05,
|
|
| 103 | + textAlign = TextAlign.Center,
|
|
| 104 | + ),
|
|
| 105 | + )
|
|
| 106 | + }
|
|
| 107 | +}
|
|
| 108 | + |
|
| 109 | +private fun getVariableWidth(width: Dp): Float =
|
|
| 110 | + (alternateLayoutThreshHold / width).coerceIn(0.80f, 1.0f)
|
|
| 111 | + |
|
| 112 | +@Composable
|
|
| 113 | +private fun PurpleBox(
|
|
| 114 | + alternateLayout: Boolean,
|
|
| 115 | + shouldShowPromo: MutableState<Boolean>,
|
|
| 116 | + onDonateButtonClicked: () -> Unit,
|
|
| 117 | + toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
|
|
| 118 | +) {
|
|
| 119 | + Box(
|
|
| 120 | + modifier = Modifier.background(
|
|
| 121 | + colorResource(mozilla.components.ui.colors.R.color.photonViolet90),
|
|
| 122 | + shape = RoundedCornerShape(8.dp),
|
|
| 123 | + ),
|
|
| 124 | + ) {
|
|
| 125 | + Row(
|
|
| 126 | + modifier = Modifier
|
|
| 127 | + .fillMaxWidth()
|
|
| 128 | + .wrapContentHeight(),
|
|
| 129 | + horizontalArrangement = Arrangement.End,
|
|
| 130 | + ) {
|
|
| 131 | + ExitIcon(shouldShowPromo)
|
|
| 132 | + }
|
|
| 133 | + DynamicCampaignContent(
|
|
| 134 | + alternateLayout, onDonateButtonClicked = onDonateButtonClicked,
|
|
| 135 | + toolPair = toolPair
|
|
| 136 | + )
|
|
| 137 | + }
|
|
| 138 | +}
|
|
| 139 | + |
|
| 140 | +@Composable
|
|
| 141 | +private fun ExitIcon(shouldShowYec: MutableState<Boolean>) {
|
|
| 142 | + IconButton(
|
|
| 143 | + modifier = Modifier.padding(8.dp),
|
|
| 144 | + onClick = {
|
|
| 145 | + shouldShowYec.value = false
|
|
| 146 | + },
|
|
| 147 | + ) {
|
|
| 148 | + Icon(
|
|
| 149 | + painter = painterResource(id = R.drawable.ic_close),
|
|
| 150 | + tint = PhotonColors.White,
|
|
| 151 | + contentDescription = stringResource(R.string.close_yec_button_description),
|
|
| 152 | + )
|
|
| 153 | + }
|
|
| 154 | +}
|
|
| 155 | + |
|
| 156 | + |
|
| 157 | +@Composable
|
|
| 158 | +private fun DynamicCampaignContent(
|
|
| 159 | + alternateLayout: Boolean,
|
|
| 160 | + onDonateButtonClicked: () -> Unit,
|
|
| 161 | + toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
|
|
| 162 | +) {
|
|
| 163 | + @Composable
|
|
| 164 | + fun Icon(shouldShow: Boolean) {
|
|
| 165 | + if (shouldShow) {
|
|
| 166 | + Image(
|
|
| 167 | + painterResource(R.drawable.globe_chain_burst_yec),
|
|
| 168 | + contentDescription = null,
|
|
| 169 | + alignment = Alignment.Center,
|
|
| 170 | + )
|
|
| 171 | + }
|
|
| 172 | + }
|
|
| 173 | + Row(
|
|
| 174 | + modifier = Modifier
|
|
| 175 | + .padding(start = 16.dp, top = 32.dp, end = 16.dp, bottom = 24.dp)
|
|
| 176 | + .fillMaxWidth(),
|
|
| 177 | + verticalAlignment = Alignment.CenterVertically,
|
|
| 178 | + ) {
|
|
| 179 | + Column(
|
|
| 180 | + modifier = Modifier.fillMaxWidth(if (alternateLayout) 0.75f else 1.0f),
|
|
| 181 | + horizontalAlignment = if (alternateLayout) Alignment.Start else Alignment.CenterHorizontally,
|
|
| 182 | + ) {
|
|
| 183 | + Icon(shouldShow = !alternateLayout)
|
|
| 184 | + Spacer(Modifier.size(24.dp))
|
|
| 185 | + TitleText()
|
|
| 186 | + Spacer(Modifier.size(16.dp))
|
|
| 187 | + MainText( toolPair = toolPair )
|
|
| 188 | + Spacer(Modifier.size(24.dp))
|
|
| 189 | + Row(
|
|
| 190 | + horizontalArrangement = if (alternateLayout) Arrangement.Start else Arrangement.Center,
|
|
| 191 | + modifier = Modifier.fillMaxWidth(),
|
|
| 192 | + ) {
|
|
| 193 | + DonateButton(onDonateButtonClicked = onDonateButtonClicked, alternateLayout)
|
|
| 194 | + }
|
|
| 195 | + }
|
|
| 196 | + Icon(shouldShow = alternateLayout)
|
|
| 197 | + }
|
|
| 198 | +}
|
|
| 199 | + |
|
| 200 | + |
|
| 201 | +@Composable
|
|
| 202 | +private fun TitleText() {
|
|
| 203 | + Text(
|
|
| 204 | + text = stringResource(R.string.summer_2026_funding_heading),
|
|
| 205 | + style = TextStyle(
|
|
| 206 | + fontSize = 24.sp,
|
|
| 207 | + lineHeight = 32.sp,
|
|
| 208 | + fontWeight = FontWeight(400),
|
|
| 209 | + color = PhotonColors.White,
|
|
| 210 | + textAlign = TextAlign.Center,
|
|
| 211 | + letterSpacing = 0.18.sp,
|
|
| 212 | + ),
|
|
| 213 | + )
|
|
| 214 | +}
|
|
| 215 | + |
|
| 216 | + |
|
| 217 | +@Composable
|
|
| 218 | +private fun MainText(toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>) {
|
|
| 219 | + Column {
|
|
| 220 | + Text(
|
|
| 221 | + AnnotatedString.fromHtml(
|
|
| 222 | + // Relevant documentation on HTML markup for android https://developer.android.com/guide/topics/resources/string-resource?utm_source=android-studio-app&utm_medium=app#StylingWithHTML
|
|
| 223 | + stringResource(
|
|
| 224 | + R.string.summer_2026_funding_intro,
|
|
| 225 | + "<b>" + toolPair.first.name + "</b>",
|
|
| 226 | + stringResource(toolPair.first.description),
|
|
| 227 | + "<b>" + toolPair.second.name + "</b>",
|
|
| 228 | + stringResource(toolPair.second.description),
|
|
| 229 | + ),
|
|
| 230 | + ), style = TextStyle(
|
|
| 231 | + fontSize = 14.sp,
|
|
| 232 | + lineHeight = 20.sp,
|
|
| 233 | + color = PhotonColors.White,
|
|
| 234 | + fontWeight = FontWeight(400),
|
|
| 235 | + letterSpacing = 0.25.sp,
|
|
| 236 | + )
|
|
| 237 | + )
|
|
| 238 | + Spacer(Modifier.size(8.dp))
|
|
| 239 | + Text(
|
|
| 240 | + text = AnnotatedString.fromHtml(
|
|
| 241 | + stringResource(
|
|
| 242 | + R.string.summer_2026_funding_outro,
|
|
| 243 | + "<b>" + stringResource(R.string.summer_2026_call_to_donate) + "</b>"
|
|
| 244 | + )
|
|
| 245 | + ),
|
|
| 246 | + modifier = Modifier.fillMaxWidth(),
|
|
| 247 | + style = TextStyle(
|
|
| 248 | + fontSize = 14.sp,
|
|
| 249 | + lineHeight = 20.sp,
|
|
| 250 | + fontWeight = FontWeight(400),
|
|
| 251 | + letterSpacing = 0.25.sp,
|
|
| 252 | + color = PhotonColors.LightGrey05,
|
|
| 253 | + )
|
|
| 254 | + )
|
|
| 255 | + }
|
|
| 256 | +}
|
|
| 257 | + |
|
| 258 | +@Composable
|
|
| 259 | +private fun DonateButton(onDonateButtonClicked: () -> Unit, alternateLayout: Boolean) {
|
|
| 260 | + Button(
|
|
| 261 | + onClick = onDonateButtonClicked,
|
|
| 262 | + colors = ButtonDefaults.buttonColors(
|
|
| 263 | + colorResource(mozilla.components.ui.colors.R.color.photonViolet60),
|
|
| 264 | + ),
|
|
| 265 | + shape = RoundedCornerShape(4.dp),
|
|
| 266 | + contentPadding = PaddingValues(horizontal = 16.dp),
|
|
| 267 | + modifier = if (alternateLayout) Modifier.wrapContentWidth() else Modifier.fillMaxWidth()
|
|
| 268 | + ) {
|
|
| 269 | + Image(
|
|
| 270 | + painterResource(R.drawable.heart),
|
|
| 271 | + contentDescription = null,
|
|
| 272 | + )
|
|
| 273 | + Spacer(
|
|
| 274 | + Modifier.size(8.dp),
|
|
| 275 | + )
|
|
| 276 | + Text(
|
|
| 277 | + text = stringResource(R.string.donate_now_yec),
|
|
| 278 | + textAlign = TextAlign.Center,
|
|
| 279 | + fontSize = 16.sp,
|
|
| 280 | + fontWeight = FontWeight.SemiBold,
|
|
| 281 | + color = PhotonColors.LightGrey05,
|
|
| 282 | + )
|
|
| 283 | + }
|
|
| 284 | +}
|
|
| 285 | + |
|
| 286 | + |
|
| 287 | + |
|
| 288 | +@Preview(
|
|
| 289 | + name = "Small Window",
|
|
| 290 | + widthDp = 400,
|
|
| 291 | +)
|
|
| 292 | +@Preview(
|
|
| 293 | + name = "Medium Window",
|
|
| 294 | + widthDp = 700,
|
|
| 295 | +)
|
|
| 296 | +@Preview(
|
|
| 297 | + name = "Large Window",
|
|
| 298 | + widthDp = 1000,
|
|
| 299 | +)
|
|
| 300 | +@Preview(
|
|
| 301 | + name = "RTL Small Window",
|
|
| 302 | + locale = "ar",
|
|
| 303 | + widthDp = 400,
|
|
| 304 | +)
|
|
| 305 | +@Preview(
|
|
| 306 | + name = "RTL Large Window",
|
|
| 307 | + locale = "ar",
|
|
| 308 | + widthDp = 1000
|
|
| 309 | +)
|
|
| 310 | +annotation class CampaignComposePreview |
| 1 | +package org.mozilla.fenix.tor
|
|
| 2 | + |
|
| 3 | +import android.app.Application
|
|
| 4 | +import android.util.Log
|
|
| 5 | +import androidx.annotation.StringRes
|
|
| 6 | +import androidx.compose.runtime.MutableState
|
|
| 7 | +import androidx.compose.runtime.mutableStateOf
|
|
| 8 | +import androidx.lifecycle.AndroidViewModel
|
|
| 9 | +import androidx.lifecycle.application
|
|
| 10 | +import org.mozilla.fenix.R
|
|
| 11 | +import org.mozilla.fenix.ext.components
|
|
| 12 | +import java.text.SimpleDateFormat
|
|
| 13 | +import java.util.Date
|
|
| 14 | + |
|
| 15 | +class TorCampaignViewModel(application: Application) : AndroidViewModel(application) {
|
|
| 16 | + |
|
| 17 | + val shouldInitiallyShowPromo: MutableState<Boolean> by lazy {
|
|
| 18 | + mutableStateOf(shouldInitiallyShowPromo())
|
|
| 19 | + }
|
|
| 20 | + |
|
| 21 | + fun shouldInitiallyShowPromo(): Boolean {
|
|
| 22 | + val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz")
|
|
| 23 | + // From https://gitlab.torproject.org/tpo/applications/tor-browser/-/work_items/44747
|
|
| 24 | + val startDate = dateFormat.parse("2026-05-19-15-UTC")
|
|
| 25 | + val endDate = dateFormat.parse("2026-06-19-00-UTC")
|
|
| 26 | + val currentDate = Date()
|
|
| 27 | + |
|
| 28 | + if (currentDate.before(startDate) || currentDate.after(endDate)) {
|
|
| 29 | + return false
|
|
| 30 | + }
|
|
| 31 | + Log.d(
|
|
| 32 | + "TorCampaignViewModel",
|
|
| 33 | + "org.mozilla.fenix.BuildConfig.BUILD_TYPE = ${org.mozilla.fenix.BuildConfig.BUILD_TYPE}"
|
|
| 34 | + )
|
|
| 35 | + return (org.mozilla.fenix.BuildConfig.BUILD_TYPE == "release") || (org.mozilla.fenix.BuildConfig.BUILD_TYPE == "debug")
|
|
| 36 | + }
|
|
| 37 | + |
|
| 38 | + companion object {
|
|
| 39 | + val toolList: List<Tool> = listOf(
|
|
| 40 | + Tool(name = "Onion Browser", description = R.string.summer_2026_funding_tool_onion_browser_description),
|
|
| 41 | + Tool(name = "Quiet", description = R.string.summer_2026_funding_tool_quiet_description),
|
|
| 42 | + Tool(name = "Ricochet Refresh", description = R.string.summer_2026_funding_tool_ricochet_refresh_description),
|
|
| 43 | + Tool(name = "SecureDrop", description = R.string.summer_2026_funding_tool_securedrop_description),
|
|
| 44 | + Tool(name = "OnionShare", description = R.string.summer_2026_funding_tool_onionshare_description),
|
|
| 45 | + Tool(name = "Digital Security Helpdesk", description = R.string.summer_2026_funding_tool_digital_security_helpdesk_description),
|
|
| 46 | + Tool(name = "Paskoocheh", description = R.string.summer_2026_funding_tool_paskoocheh_description),
|
|
| 47 | + Tool(name = "Unredacted", description = R.string.summer_2026_funding_tool_unredacted_description),
|
|
| 48 | + Tool(name = "Osservatorio Nessuno", description = R.string.summer_2026_funding_tool_osservatorio_nessuno_description),
|
|
| 49 | + Tool(name = "Save", description = R.string.summer_2026_funding_tool_save_description),
|
|
| 50 | + Tool(name = "OONI", description = R.string.summer_2026_funding_tool_ooni_description),
|
|
| 51 | + ).shuffled()
|
|
| 52 | + }
|
|
| 53 | + |
|
| 54 | + private var toolIndex = 0
|
|
| 55 | + private fun incrementToolIndex() {
|
|
| 56 | + toolIndex = (toolIndex + 2) % toolList.size
|
|
| 57 | + }
|
|
| 58 | + |
|
| 59 | + data class Tool(
|
|
| 60 | + val name: String,
|
|
| 61 | + @param:StringRes val description: Int,
|
|
| 62 | + )
|
|
| 63 | + |
|
| 64 | + fun getToolPair() : Pair<Tool, Tool> {
|
|
| 65 | + return Pair(toolList[toolIndex], toolList[(toolIndex + 1) % toolList.size]).also { incrementToolIndex() }
|
|
| 66 | + }
|
|
| 67 | +} |
| 1 | 1 | package org.mozilla.fenix.tor
|
| 2 | 2 | |
| 3 | 3 | import androidx.compose.foundation.Image
|
| 4 | +import androidx.compose.foundation.layout.Box
|
|
| 4 | 5 | import androidx.compose.foundation.layout.Column
|
| 5 | 6 | import androidx.compose.foundation.layout.Row
|
| 6 | 7 | import androidx.compose.foundation.layout.Spacer
|
| ... | ... | @@ -12,6 +13,10 @@ import androidx.compose.foundation.rememberScrollState |
| 12 | 13 | import androidx.compose.foundation.verticalScroll
|
| 13 | 14 | import androidx.compose.material.Text
|
| 14 | 15 | import androidx.compose.runtime.Composable
|
| 16 | +import androidx.compose.runtime.MutableState
|
|
| 17 | +import androidx.compose.runtime.mutableStateOf
|
|
| 18 | +import androidx.compose.runtime.remember
|
|
| 19 | +import androidx.compose.runtime.saveable.rememberSaveable
|
|
| 15 | 20 | import androidx.compose.ui.Alignment
|
| 16 | 21 | import androidx.compose.ui.Modifier
|
| 17 | 22 | import androidx.compose.ui.draw.paint
|
| ... | ... | @@ -26,16 +31,33 @@ import androidx.compose.ui.res.stringResource |
| 26 | 31 | import androidx.compose.ui.text.TextStyle
|
| 27 | 32 | import androidx.compose.ui.text.font.FontWeight
|
| 28 | 33 | import androidx.compose.ui.text.style.TextAlign
|
| 34 | +import androidx.compose.ui.tooling.preview.Preview
|
|
| 35 | +import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
| 36 | +import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
| 29 | 37 | import androidx.compose.ui.unit.dp
|
| 30 | 38 | import androidx.compose.ui.unit.sp
|
| 31 | -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
|
|
| 32 | 39 | import org.mozilla.fenix.R
|
| 40 | +import org.mozilla.fenix.home.ui.SearchBarPreview
|
|
| 33 | 41 | |
| 34 | 42 | @Composable
|
| 35 | -@FlexibleWindowLightDarkPreview
|
|
| 36 | 43 | fun TorHomePage(
|
| 37 | - toolBarAtTop: Boolean = true,
|
|
| 44 | + shouldInitiallyShowPromo: MutableState<Boolean> = mutableStateOf(true),
|
|
| 45 | + onClicked: () -> Unit = {},
|
|
| 46 | + toolBarAtTop: Boolean = false,
|
|
| 47 | + toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
|
|
| 38 | 48 | ) {
|
| 49 | + // Will persist across a single session, but not multiple sessions.
|
|
| 50 | + // Tapping the close button 'X' will hide the promo for the duration of the session
|
|
| 51 | + val shouldShowPromo = rememberSaveable {
|
|
| 52 | + shouldInitiallyShowPromo
|
|
| 53 | + }
|
|
| 54 | + |
|
| 55 | + // Will persist through screen rotations, but not navigations (e.g. Tap on settings -> come back will update)
|
|
| 56 | + // Is expected to change with every new visit to about:tor
|
|
| 57 | + val toolPair = remember {
|
|
| 58 | + toolPair
|
|
| 59 | + }
|
|
| 60 | + |
|
| 39 | 61 | Column(
|
| 40 | 62 | modifier = Modifier
|
| 41 | 63 | .fillMaxSize()
|
| ... | ... | @@ -82,30 +104,78 @@ fun TorHomePage( |
| 82 | 104 | )
|
| 83 | 105 | }
|
| 84 | 106 | Spacer(Modifier.weight(1f))
|
| 85 | - Text(
|
|
| 86 | - // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2
|
|
| 87 | - // "[android] Modify UI/UX", and the file HomeFragment.
|
|
| 88 | - // Splits by full stops or commas and puts the parts in different lines.
|
|
| 89 | - // Ignoring separators at the end of the string, it is expected
|
|
| 90 | - // that there are at most two parts (e.g. "Explore. Privately.").
|
|
| 91 | - text = stringResource(R.string.tor_explore_privately).replace(
|
|
| 107 | + if (shouldShowPromo.value) {
|
|
| 108 | + CampaignBox(
|
|
| 109 | + shouldShowPromo,
|
|
| 110 | + onDonateButtonClicked = onClicked,
|
|
| 111 | + toolPair = toolPair
|
|
| 112 | + )
|
|
| 113 | + Spacer(Modifier.weight(1f))
|
|
| 114 | + } else {
|
|
| 115 | + Text(
|
|
| 116 | + // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2
|
|
| 117 | + // "[android] Modify UI/UX", and the file HomeFragment.
|
|
| 118 | + // Splits by full stops or commas and puts the parts in different lines.
|
|
| 119 | + // Ignoring separators at the end of the string, it is expected
|
|
| 120 | + // that there are at most two parts (e.g. "Explore. Privately.").
|
|
| 121 | + text = stringResource(R.string.tor_explore_privately).replace(
|
|
| 92 | 122 | " *([.,。।]) *".toRegex(),
|
| 93 | 123 | "$1\n",
|
| 94 | 124 | ).trim(),
|
| 95 | - style = TextStyle(
|
|
| 96 | - color = Color(color = 0xDEFFFFFF),
|
|
| 97 | - fontSize = 40.sp,
|
|
| 98 | - textAlign = TextAlign.Start,
|
|
| 99 | - ),
|
|
| 100 | - modifier = Modifier.align(Alignment.CenterHorizontally),
|
|
| 101 | - )
|
|
| 102 | - Spacer(Modifier.weight(1f))
|
|
| 103 | - Image(
|
|
| 104 | - painter = painterResource(
|
|
| 105 | - id = R.drawable.ic_onion_pattern,
|
|
| 106 | - ),
|
|
| 107 | - contentDescription = null, Modifier.fillMaxWidth(),
|
|
| 108 | - )
|
|
| 125 | + style = TextStyle(
|
|
| 126 | + color = Color(color = 0xDEFFFFFF),
|
|
| 127 | + fontSize = 40.sp,
|
|
| 128 | + textAlign = TextAlign.Start,
|
|
| 129 | + ),
|
|
| 130 | + modifier = Modifier.align(Alignment.CenterHorizontally),
|
|
| 131 | + )
|
|
| 132 | + }
|
|
| 133 | + if (!shouldShowPromo.value) {
|
|
| 134 | + Spacer(Modifier.weight(1f))
|
|
| 135 | + Image(
|
|
| 136 | + painter = painterResource(
|
|
| 137 | + id = R.drawable.ic_onion_pattern,
|
|
| 138 | + ),
|
|
| 139 | + contentDescription = null, Modifier.fillMaxWidth(),
|
|
| 140 | + )
|
|
| 141 | + }
|
|
| 109 | 142 | }
|
| 110 | 143 | Spacer(modifier = Modifier.size(17.dp))
|
| 111 | 144 | }
|
| 145 | + |
|
| 146 | +@Composable
|
|
| 147 | +@Preview
|
|
| 148 | +/**
|
|
| 149 | + * Relevant documentation
|
|
| 150 | + * https://developer.android.com/develop/ui/compose/tooling/previews#preview-viewmodel
|
|
| 151 | + */
|
|
| 152 | +private fun TorHomePagePreview(
|
|
| 153 | + @PreviewParameter(
|
|
| 154 | + BooleanBooleanPreviewParameterProvider::class,
|
|
| 155 | + ) booleanMatrix: Pair<Boolean, Boolean>,
|
|
| 156 | +) {
|
|
| 157 | + val toolbarAtTop = booleanMatrix.second
|
|
| 158 | + Box(
|
|
| 159 | + contentAlignment = if (toolbarAtTop) Alignment.TopStart else Alignment.BottomStart,
|
|
| 160 | + modifier = Modifier.fillMaxSize(),
|
|
| 161 | + ) {
|
|
| 162 | + SearchBarPreview() // unrestricted vertically so will follow contentAlignment
|
|
| 163 | + TorHomePage(
|
|
| 164 | + // restricted vertically so will not follow contentAlignment
|
|
| 165 | + shouldInitiallyShowPromo = mutableStateOf(booleanMatrix.first),
|
|
| 166 | + toolBarAtTop = toolbarAtTop,
|
|
| 167 | + toolPair = Pair(TorCampaignViewModel.toolList[0], TorCampaignViewModel.toolList[1]),
|
|
| 168 | + )
|
|
| 169 | + }
|
|
| 170 | +}
|
|
| 171 | + |
|
| 172 | +private class BooleanBooleanPreviewParameterProvider :
|
|
| 173 | + PreviewParameterProvider<Pair<Boolean, Boolean>> {
|
|
| 174 | + override val values: Sequence<Pair<Boolean, Boolean>>
|
|
| 175 | + get() = sequenceOf(
|
|
| 176 | + Pair(true, true),
|
|
| 177 | + Pair(true, false),
|
|
| 178 | + Pair(false, true),
|
|
| 179 | + Pair(false, false),
|
|
| 180 | + )
|
|
| 181 | +} |
| 1 | +<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">
|
|
| 2 | +
|
|
| 3 | + <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"/>
|
|
| 4 | +
|
|
| 5 | + <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"/>
|
|
| 6 | +
|
|
| 7 | + <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"/>
|
|
| 8 | +
|
|
| 9 | + <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M62.37,70.25H60.98V68.84H62.37V70.25Z" android:strokeAlpha="0.8"/>
|
|
| 10 | +
|
|
| 11 | + <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M95.1,67.44H93.7V55.17H95.1V67.44Z" android:strokeAlpha="0.8"/>
|
|
| 12 | +
|
|
| 13 | + <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"/>
|
|
| 14 | +
|
|
| 15 | + <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"/>
|
|
| 16 | +
|
|
| 17 | + <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"/>
|
|
| 18 | +
|
|
| 19 | + <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"/>
|
|
| 20 | +
|
|
| 21 | + <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"/>
|
|
| 22 | +
|
|
| 23 | + <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"/>
|
|
| 24 | +
|
|
| 25 | + <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"/>
|
|
| 26 | +
|
|
| 27 | + <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"/>
|
|
| 28 | +
|
|
| 29 | + <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"/>
|
|
| 30 | +
|
|
| 31 | + <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"/>
|
|
| 32 | +
|
|
| 33 | + <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"/>
|
|
| 34 | +
|
|
| 35 | +</vector> |
| 1 | +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="16dp" android:viewportHeight="16" android:viewportWidth="16" android:width="16dp">
|
|
| 2 | +
|
|
| 3 | + <path android:fillColor="#FBFBFE" android:pathData="M8,6C8,6 8,2 11.5,2C15,2 15,5 15,6C15,10.5 8,15 8,15V6Z"/>
|
|
| 4 | +
|
|
| 5 | + <path android:fillColor="#FBFBFE" android:pathData="M8,6C8,6 8,2 4.5,2C1,2 1,5 1,6C1,10.5 8,15 8,15L9,9L8,6Z"/>
|
|
| 6 | +
|
|
| 7 | +</vector> |
| ... | ... | @@ -153,4 +153,36 @@ |
| 153 | 153 | <!-- Year End Campaign. Here "Close" is a verb, referring to closing the banner. This is used primarily for screen readers to give the small "x" button a name. -->
|
| 154 | 154 | <string name="close_yec_button_description">Close</string>
|
| 155 | 155 | |
| 156 | + <!-- Summer 2026 Tor Project funding campaign. -->
|
|
| 157 | + |
|
| 158 | + <string name="summer_2026_funding_heading">A new way to fund internet freedom</string>
|
|
| 159 | + <!-- '%1$s' will be replaced with the name of some internet freedom tool, this is not translated and will only use the Latin alphabet, and will be made to appear bold. Similarly, '%3$s' will be replaced with the name of some other internet freedom tool. '%2$s' and '%4$s' will be replaced with the respective descriptions for the tools, this is translated. Note, since '%2$s' and '%4$s' will contain arbitrary text, you will likely need to separate them out from the rest of the text. The English text uses brackets '(' and ')' to do this. -->
|
|
| 160 | + <string name="summer_2026_funding_intro">Did you know that internet freedom tools serve millions of people every day? This includes tools like %1$s (%2$s) and %3$s (%4$s).</string>
|
|
| 161 | + <!-- '%s' will be replaced with summer_2026_call_to_donate and will be made bold. Here "Tor" refers to The Tor Project. -->
|
|
| 162 | + <string name="summer_2026_funding_outro">But unprecedented funding cuts have harmed the small organizations that maintain these tools. %s—all donations will be matched by friends of Tor.</string>
|
|
| 163 | + <!-- This will replace '%s' in summer_2026_funding_outro and will be made bold -->
|
|
| 164 | + <string name="summer_2026_call_to_donate">Help protect internet freedom by donating cryptocurrency</string>
|
|
| 165 | + <!-- A short description of "Onion Browser": onionbrowser.com -->
|
|
| 166 | + <string name="summer_2026_funding_tool_onion_browser_description">Tor-powered browser for iOS</string>
|
|
| 167 | + <!-- A short description of "Quiet": tryquiet.org -->
|
|
| 168 | + <string name="summer_2026_funding_tool_quiet_description">private group messaging</string>
|
|
| 169 | + <!-- A short description of "Ricochet Refresh": ricochetrefresh.net -->
|
|
| 170 | + <string name="summer_2026_funding_tool_ricochet_refresh_description">secure and anonymous messaging</string>
|
|
| 171 | + <!-- A short description of "SecureDrop": securedrop.org -->
|
|
| 172 | + <string name="summer_2026_funding_tool_securedrop_description">anonymous whistleblowing app</string>
|
|
| 173 | + <!-- A short description of "OnionShare": onionshare.org. "&" shows up as "&" and means "and" in english. -->
|
|
| 174 | + <string name="summer_2026_funding_tool_onionshare_description">secure filesharing, hosting & chatting</string>
|
|
| 175 | + <!-- A short description of "Digital Security Helpdesk": miaan.org/projects/miaan-digital-security-helpdesk -->
|
|
| 176 | + <string name="summer_2026_funding_tool_digital_security_helpdesk_description">internet freedom for Iranians</string>
|
|
| 177 | + <!-- A short description of "Paskoocheh": paskoocheh.com -->
|
|
| 178 | + <string name="summer_2026_funding_tool_paskoocheh_description">countering digital authoritarianism</string>
|
|
| 179 | + <!-- A short description of "Unredacted": unredacted.org -->
|
|
| 180 | + <string name="summer_2026_funding_tool_unredacted_description">keeping people connected, no matter where</string>
|
|
| 181 | + <!-- A short description of "Osservatorio Nessuno": osservatorionessuno.org -->
|
|
| 182 | + <string name="summer_2026_funding_tool_osservatorio_nessuno_description">software that leaves no trace</string>
|
|
| 183 | + <!-- A short description of "Save": open-archive.org/save -->
|
|
| 184 | + <string name="summer_2026_funding_tool_save_description">private archiving</string>
|
|
| 185 | + <!-- A short description of "OONI": ooni.org -->
|
|
| 186 | + <string name="summer_2026_funding_tool_ooni_description">tracking censorship globally</string>
|
|
| 187 | + |
|
| 156 | 188 | </resources> |