GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

Commit 3cff3fb2 authored by Matthew Finkel's avatar Matthew Finkel

Bug 40028: Implement Tor Onboarding

parent bb846bdc
...@@ -36,6 +36,8 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTh ...@@ -36,6 +36,8 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTh
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingWhatsNewViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingWhatsNewViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.TorOnboardingSecurityLevelViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.TorOnboardingDonateViewHolder
import org.mozilla.fenix.home.tips.ButtonTipViewHolder import org.mozilla.fenix.home.tips.ButtonTipViewHolder
import mozilla.components.feature.tab.collections.Tab as ComponentTab import mozilla.components.feature.tab.collections.Tab as ComponentTab
...@@ -113,6 +115,9 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) { ...@@ -113,6 +115,9 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
object OnboardingWhatsNew : AdapterItem(OnboardingWhatsNewViewHolder.LAYOUT_ID) object OnboardingWhatsNew : AdapterItem(OnboardingWhatsNewViewHolder.LAYOUT_ID)
object TorOnboardingSecurityLevel : AdapterItem(TorOnboardingSecurityLevelViewHolder.LAYOUT_ID)
object TorOnboardingDonate : AdapterItem(TorOnboardingDonateViewHolder.LAYOUT_ID)
/** /**
* True if this item represents the same value as other. Used by [AdapterItemDiffCallback]. * True if this item represents the same value as other. Used by [AdapterItemDiffCallback].
*/ */
...@@ -197,6 +202,14 @@ class SessionControlAdapter( ...@@ -197,6 +202,14 @@ class SessionControlAdapter(
OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder( OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder(
view view
) )
TorOnboardingSecurityLevelViewHolder.LAYOUT_ID -> TorOnboardingSecurityLevelViewHolder(
view,
interactor
)
TorOnboardingDonateViewHolder.LAYOUT_ID -> TorOnboardingDonateViewHolder(
view,
interactor
)
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
......
...@@ -110,6 +110,11 @@ interface SessionControlController { ...@@ -110,6 +110,11 @@ interface SessionControlController {
*/ */
fun handleOpenSettingsClicked() fun handleOpenSettingsClicked()
/**
* @see [OnboardingInteractor.onOpenSecurityLevelSettingsClicked]
*/
fun handleOpenSecurityLevelSettingsClicked()
/** /**
* @see [OnboardingInteractor.onWhatsNewGetAnswersClicked] * @see [OnboardingInteractor.onWhatsNewGetAnswersClicked]
*/ */
...@@ -120,6 +125,11 @@ interface SessionControlController { ...@@ -120,6 +125,11 @@ interface SessionControlController {
*/ */
fun handleReadPrivacyNoticeClicked() fun handleReadPrivacyNoticeClicked()
/**
* @see [OnboardingInteractor.onDonateClicked]
*/
fun handleDonateClicked()
/** /**
* @see [CollectionInteractor.onToggleCollectionExpanded] * @see [CollectionInteractor.onToggleCollectionExpanded]
*/ */
...@@ -359,6 +369,9 @@ class DefaultSessionControlController( ...@@ -359,6 +369,9 @@ class DefaultSessionControlController(
navController.nav(R.id.homeFragment, directions) navController.nav(R.id.homeFragment, directions)
} }
override fun handleOpenSecurityLevelSettingsClicked() {
}
override fun handleWhatsNewGetAnswersClicked() { override fun handleWhatsNewGetAnswersClicked() {
activity.openToBrowserAndLoad( activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getWhatsNewUrl(activity), searchTermOrURL = SupportUtils.getWhatsNewUrl(activity),
...@@ -375,6 +388,14 @@ class DefaultSessionControlController( ...@@ -375,6 +388,14 @@ class DefaultSessionControlController(
) )
} }
override fun handleDonateClicked() {
activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.DONATE_URL,
newTab = true,
from = BrowserDirection.FromHome
)
}
override fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean) { override fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
fragmentStore.dispatch(HomeFragmentAction.CollectionExpanded(collection, expand)) fragmentStore.dispatch(HomeFragmentAction.CollectionExpanded(collection, expand))
} }
......
...@@ -126,6 +126,11 @@ interface OnboardingInteractor { ...@@ -126,6 +126,11 @@ interface OnboardingInteractor {
*/ */
fun onOpenSettingsClicked() fun onOpenSettingsClicked()
/**
* Hides the onboarding and navigates to Settings. Called when a user clicks on the "Open Security Settings" button.
*/
fun onOpenSecurityLevelSettingsClicked()
/** /**
* Opens a custom tab to what's new url. Called when a user clicks on the "Get answers here" link. * Opens a custom tab to what's new url. Called when a user clicks on the "Get answers here" link.
*/ */
...@@ -135,6 +140,11 @@ interface OnboardingInteractor { ...@@ -135,6 +140,11 @@ interface OnboardingInteractor {
* Opens a custom tab to privacy notice url. Called when a user clicks on the "read our privacy notice" button. * Opens a custom tab to privacy notice url. Called when a user clicks on the "read our privacy notice" button.
*/ */
fun onReadPrivacyNoticeClicked() fun onReadPrivacyNoticeClicked()
/**
* Opens a custom tab to privacy notice url. Called when a user clicks on the "read our privacy notice" button.
*/
fun onDonateClicked()
} }
interface TipInteractor { interface TipInteractor {
...@@ -258,6 +268,10 @@ class SessionControlInteractor( ...@@ -258,6 +268,10 @@ class SessionControlInteractor(
controller.handleOpenSettingsClicked() controller.handleOpenSettingsClicked()
} }
override fun onOpenSecurityLevelSettingsClicked() {
controller.handleOpenSecurityLevelSettingsClicked()
}
override fun onWhatsNewGetAnswersClicked() { override fun onWhatsNewGetAnswersClicked() {
controller.handleWhatsNewGetAnswersClicked() controller.handleWhatsNewGetAnswersClicked()
} }
...@@ -266,6 +280,10 @@ class SessionControlInteractor( ...@@ -266,6 +280,10 @@ class SessionControlInteractor(
controller.handleReadPrivacyNoticeClicked() controller.handleReadPrivacyNoticeClicked()
} }
override fun onDonateClicked() {
controller.handleDonateClicked()
}
override fun onToggleCollectionExpanded(collection: TabCollection, expand: Boolean) { override fun onToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
controller.handleToggleCollectionExpanded(collection, expand) controller.handleToggleCollectionExpanded(collection, expand)
} }
......
...@@ -108,6 +108,13 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt ...@@ -108,6 +108,13 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
return items return items
} }
private fun torOnboardingAdapterItems() =
listOf(
AdapterItem.TorOnboardingSecurityLevel,
AdapterItem.TorOnboardingDonate,
AdapterItem.OnboardingFinish
)
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) { private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
is Mode.Normal -> normalModeAdapterItems( is Mode.Normal -> normalModeAdapterItems(
topSites, topSites,
...@@ -117,7 +124,7 @@ private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) { ...@@ -117,7 +124,7 @@ private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
showCollectionPlaceholder showCollectionPlaceholder
) )
is Mode.Private -> privateModeAdapterItems() is Mode.Private -> privateModeAdapterItems()
is Mode.Onboarding -> onboardingAdapterItems(mode.state) is Mode.Onboarding -> torOnboardingAdapterItems()
is Mode.Bootstrap -> bootstrapAdapterItems() is Mode.Bootstrap -> bootstrapAdapterItems()
} }
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_onboarding_donate.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
class TorOnboardingDonateViewHolder(
view: View,
private val interactor: OnboardingInteractor
) : RecyclerView.ViewHolder(view) {
init {
view.donate_now_button.setOnClickListener {
interactor.onDonateClicked()
}
}
companion object {
const val LAYOUT_ID = R.layout.tor_onboarding_donate
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_onboarding_security_level.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
class TorOnboardingSecurityLevelViewHolder(
view: View,
private val interactor: OnboardingInteractor
) : RecyclerView.ViewHolder(view) {
private var standardSecurityLevel: OnboardingRadioButton
private var saferSecurityLevel: OnboardingRadioButton
private var safestSecurityLevel: OnboardingRadioButton
init {
view.header_text.setOnboardingIcon(R.drawable.ic_onboarding_tracking_protection)
standardSecurityLevel = view.security_level_standard_option
saferSecurityLevel = view.security_level_safer_option
safestSecurityLevel = view.security_level_safest_option
view.description_text.text = view.context.getString(
R.string.tor_onboarding_security_level_description
)
view.open_settings_button.setOnClickListener {
interactor.onOpenSettingsClicked()
}
setupRadioGroup()
}
private fun setupRadioGroup() {
addToRadioGroup(standardSecurityLevel, saferSecurityLevel, safestSecurityLevel)
standardSecurityLevel.isChecked = true
safestSecurityLevel.isChecked = false
saferSecurityLevel.isChecked = false
standardSecurityLevel.onClickListener {
updateSecurityLevel()
}
saferSecurityLevel.onClickListener {
updateSecurityLevel()
}
safestSecurityLevel.onClickListener {
updateSecurityLevel()
}
}
private fun updateSecurityLevel() {
}
companion object {
const val LAYOUT_ID = R.layout.tor_onboarding_security_level
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="45"
android:startColor="#07d1db"
android:endColor="#b240f5"
android:type="linear" />
</shape>
</item>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- Used for rounding the corners of a button -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#70efde" />
<corners android:radius="10dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/onboarding_card"
style="@style/TorOnboardingDonateCardLightWithPadding"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/header_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/tor_onboarding_donate_header"
android:textAppearance="@style/TorHeaderTextStyle"
android:gravity="center_vertical"
android:lines="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/description_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TorBody16TextStyle"
android:layout_marginTop="14dp"
android:text="@string/tor_onboarding_donate_description"
app:layout_constraintTop_toBottomOf="@id/header_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
style="@style/TorDonateOnboardingButton"
android:id="@+id/donate_now_button"
android:text="@string/tor_onboarding_donate_button"
android:layout_marginTop="10dp"
android:textAppearance="@style/TorHeaderTextStyle"
android:background="@drawable/tor_onboarding_donate_rounded_corners"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/onboarding_card"
style="@style/OnboardingCardLightWithPadding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<TextView
android:id="@+id/header_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:gravity="center_vertical"
android:lines="1"
android:text="@string/tor_onboarding_security_level"
android:textAppearance="@style/HeaderTextStyle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:drawableStart="@drawable/ic_onboarding_tracking_protection" />
<TextView
android:id="@+id/description_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/header_text"
tools:text="@string/tor_onboarding_security_level_description" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/security_level_standard_option"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:checked="true"
android:foreground="@drawable/rounded_ripple"
android:gravity="top"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:theme="@style/Checkable.Colored"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/description_text"
app:onboardingKey="@string/pref_key_tor_security_level_standard_option"
app:onboardingKeyDescription="@string/tor_security_level_standard_description"
app:onboardingKeyTitle="@string/tor_security_level_standard_option"
tools:text="Standard" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/security_level_safer_option"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:checked="false"
android:foreground="@drawable/rounded_ripple"
android:gravity="top"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/primary_state_list_text_color"
android:theme="@style/Checkable.Colored"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/security_level_standard_option"
app:onboardingKey="@string/pref_key_tor_security_level_safer_option"
app:onboardingKeyDescription="@string/tor_security_level_safer_description"
app:onboardingKeyTitle="@string/tor_security_level_safer_option"
tools:text="Safer" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/security_level_safest_option"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:checked="false"
android:foreground="@drawable/rounded_ripple"
android:gravity="top"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/primary_state_list_text_color"
android:theme="@style/Checkable.Colored"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/security_level_safer_option"
app:onboardingKey="@string/pref_key_tor_security_level_safest_option"
app:onboardingKeyDescription="@string/tor_security_level_safest_description"
app:onboardingKeyTitle="@string/tor_security_level_safest_option"
tools:text="Safest" />
<Button
android:id="@+id/open_settings_button"
style="@style/NeutralOnboardingButton"
android:layout_marginTop="16dp"
android:text="@string/tor_onboarding_security_settings_button"
app:layout_constraintTop_toBottomOf="@id/security_level_safest_option"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
...@@ -241,4 +241,10 @@ ...@@ -241,4 +241,10 @@
<string name="pref_key_return_to_browser" translatable="false">pref_key_return_to_browser</string> <string name="pref_key_return_to_browser" translatable="false">pref_key_return_to_browser</string>
<string name="pref_key_noscript_installed" translatable="false">pref_key_noscript_installed</string> <string name="pref_key_noscript_installed" translatable="false">pref_key_noscript_installed</string>
<!-- Security Level Settings -->
<string name="pref_key_tor_security_level_settings" translatable="false">pref_key_tor_security_level_settings</string>
<string name="pref_key_tor_security_level_standard_default" translatable="false">pref_key_tor_security_level_standard_default</string>
<string name="pref_key_tor_security_level_safer_option" translatable="false">pref_key_tor_security_level_safer_option</string>
<string name="pref_key_tor_security_level_safest_option" translatable="false">pref_key_tor_security_level_safest_option</string>
</resources> </resources>
...@@ -299,6 +299,13 @@ ...@@ -299,6 +299,13 @@
<item name="android:textColor">?primaryText</item> <item name="android:textColor">?primaryText</item>
</style> </style>
<!-- Ideally we should consolidate this with NeutralButton in the future -->
<style name="TorDonateOnboardingButton" parent="NeutralButton">
<item name="android:background">@drawable/tor_onboarding_donate_rounded_corners</item>
<item name="backgroundTint">#70efde</item>
<item name="android:textColor">#000000</item>
</style>
<style name="DestructiveButton" parent="NeutralButton"> <style name="DestructiveButton" parent="NeutralButton">
<item name="iconTint">@color/destructive_button_text_color</item> <item name="iconTint">@color/destructive_button_text_color</item>
<item name="android:textColor">@color/destructive_button_text_color</item> <item name="android:textColor">@color/destructive_button_text_color</item>
...@@ -377,6 +384,12 @@ ...@@ -377,6 +384,12 @@
<item name="fontFamily">@font/metropolis_semibold</item> <item name="fontFamily">@font/metropolis_semibold</item>
</style> </style>
<style name="TorHeaderTextStyle" parent="TextAppearance.MaterialComponents.Subtitle1">
<item name="android:textSize">18sp</item>
<item name="android:textColor">#000000</item>
<item name="fontFamily">@font/metropolis_semibold</item>
</style>
<style name="Header16TextStyle" parent="TextAppearance.MaterialComponents.Body1"> <style name="Header16TextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">?primaryText</item> <item name="android:textColor">?primaryText</item>
<item name="android:textSize">16sp</item> <item name="android:textSize">16sp</item>
...@@ -405,6 +418,10 @@ ...@@ -405,6 +418,10 @@
<item name="android:textColor">?primaryText</item> <item name="android:textColor">?primaryText</item>
</style> </style>
<style name="TorBody16TextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">#000000</item>
</style>
<style name="SubtitleTextStyle" parent="TextAppearance.MaterialComponents.Body1"> <style name="SubtitleTextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">?secondaryText</item> <item name="android:textColor">?secondaryText</item>
<item name="android:textSize">14sp</item> <item name="android:textSize">14sp</item>
...@@ -487,6 +504,10 @@ ...@@ -487,6 +504,10 @@
<item name="android:elevation">0dp</item> <item name="android:elevation">0dp</item>
</style> </style>
<style name="TorOnboardingDonateCardLightWithPadding" parent="OnboardingCardDark">
<item name="android:background">@drawable/tor_onboarding_donate_gradient</item>
</style>
<style name="SearchEngineShortcutsLabelStyle"> <style name="SearchEngineShortcutsLabelStyle">
<item name="android:fontFamily">@font/metropolis_semibold</item> <item name="android:fontFamily">@font/metropolis_semibold</item>
<item name="android:letterSpacing">0.15</item> <item name="android:letterSpacing">0.15</item>
......
...@@ -17,4 +17,21 @@ ...@@ -17,4 +17,21 @@
<string name="tor_bootstrap_quick_start_enabled">%s will connect automatically to the Tor Network in the future</string> <string name="tor_bootstrap_quick_start_enabled">%s will connect automatically to the Tor Network in the future</string>
<string name="tor_bootstrap_swipe_for_logs">Swipe to the left to see Tor logs</string>