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 cf3e1c5b authored by Matthew Finkel's avatar Matthew Finkel

Bug 40028: Implement Tor Onboarding

parent 232247fd
......@@ -35,6 +35,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.OnboardingTrackingProtectionViewHolder
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 mozilla.components.feature.tab.collections.Tab as ComponentTab
......@@ -112,6 +114,9 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
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].
*/
......@@ -197,6 +202,14 @@ class SessionControlAdapter(
OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder(
view
)
TorOnboardingSecurityLevelViewHolder.LAYOUT_ID -> TorOnboardingSecurityLevelViewHolder(
view,
interactor
)
TorOnboardingDonateViewHolder.LAYOUT_ID -> TorOnboardingDonateViewHolder(
view,
interactor
)
else -> throw IllegalStateException()
}
}
......
......@@ -110,6 +110,11 @@ interface SessionControlController {
*/
fun handleOpenSettingsClicked()
/**
* @see [OnboardingInteractor.onOpenSecurityLevelSettingsClicked]
*/
fun handleOpenSecurityLevelSettingsClicked()
/**
* @see [OnboardingInteractor.onWhatsNewGetAnswersClicked]
*/
......@@ -120,6 +125,11 @@ interface SessionControlController {
*/
fun handleReadPrivacyNoticeClicked()
/**
* @see [OnboardingInteractor.onDonateClicked]
*/
fun handleDonateClicked()
/**
* @see [CollectionInteractor.onToggleCollectionExpanded]
*/
......@@ -359,6 +369,9 @@ class DefaultSessionControlController(
navController.nav(R.id.homeFragment, directions)
}
override fun handleOpenSecurityLevelSettingsClicked() {
}
override fun handleWhatsNewGetAnswersClicked() {
activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getWhatsNewUrl(activity),
......@@ -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) {
fragmentStore.dispatch(HomeFragmentAction.CollectionExpanded(collection, expand))
}
......
......@@ -126,6 +126,11 @@ interface OnboardingInteractor {
*/
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.
*/
......@@ -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.
*/
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 {
......@@ -258,6 +268,10 @@ class SessionControlInteractor(
controller.handleOpenSettingsClicked()
}
override fun onOpenSecurityLevelSettingsClicked() {
controller.handleOpenSecurityLevelSettingsClicked()
}
override fun onWhatsNewGetAnswersClicked() {
controller.handleWhatsNewGetAnswersClicked()
}
......@@ -266,6 +280,10 @@ class SessionControlInteractor(
controller.handleReadPrivacyNoticeClicked()
}
override fun onDonateClicked() {
controller.handleDonateClicked()
}
override fun onToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
controller.handleToggleCollectionExpanded(collection, expand)
}
......
......@@ -109,6 +109,13 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
return items
}
private fun torOnboardingAdapterItems() =
listOf(
AdapterItem.TorOnboardingSecurityLevel,
AdapterItem.TorOnboardingDonate,
AdapterItem.OnboardingFinish
)
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
is Mode.Normal -> normalModeAdapterItems(
topSites,
......@@ -118,7 +125,7 @@ private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
showCollectionPlaceholder
)
is Mode.Private -> privateModeAdapterItems()
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
is Mode.Onboarding -> torOnboardingAdapterItems()
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>
......@@ -231,4 +231,10 @@
<string name="pref_key_camera_permissions_needed" translatable="false">pref_key_camera_permissions_needed</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>
......@@ -301,6 +301,13 @@
<item name="android:textColor">?primaryText</item>
</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"></item>
<item name="android:textColor">#000000</item>
</style>
<style name="DestructiveButton" parent="NeutralButton">
<item name="iconTint">@color/destructive_button_text_color</item>
<item name="android:textColor">@color/destructive_button_text_color</item>
......@@ -379,6 +386,12 @@
<item name="fontFamily">@font/metropolis_semibold</item>
</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">
<item name="android:textColor">?primaryText</item>
<item name="android:textSize">16sp</item>
......@@ -407,6 +420,10 @@
<item name="android:textColor">?primaryText</item>
</style>
<style name="TorBody16TextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">#000000</item>
</style>
<style name="SubtitleTextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">?secondaryText</item>
<item name="android:textSize">14sp</item>
......@@ -489,6 +506,10 @@
<item name="android:elevation">0dp</item>
</style>
<style name="TorOnboardingDonateCardLightWithPadding" parent="OnboardingCardDark">
<item name="android:background">@drawable/tor_onboarding_donate_gradient</item>
</style>
<style name="SearchEngineShortcutsLabelStyle">
<item name="android:fontFamily">@font/metropolis_semibold</item>
<item name="android:letterSpacing">0.15</item>
......
......@@ -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_swipe_for_logs">Swipe to the left to see Tor logs</string>
<string name="tor_initializing_log">Initializing Tor Log</string>
<string name="tor_onboarding_security_level">Set your Security Level</string>
<string name="tor_onboarding_security_level_description">Disable certain web features that can be used to attack you, and harm your security, anonymity, and privacy.</string>
<string name="tor_onboarding_security_settings_button">Open Security Settings</string>
<string name="tor_onboarding_donate_header">Donate and keep Tor safe</string>
<string name="tor_onboarding_donate_description">Tor is free to use because of donations from people like you.</string>
<string name="tor_onboarding_donate_button">Donate Now</string>
<string name="tor_explore_privately">Explore.\nPrivately.</string>
<!-- Description of security levels -->
<string name="tor_security_level_standard_option">Standard</string>
<string name="tor_security_level_standard_description">All Tor Browser and website features are enabled.</string>
<string name="tor_security_level_safer_option">Safer</string>
<string name="tor_security_level_safer_description">Disable website features that are often dangerous, causing some sites to lose functionality.</string>
<string name="tor_security_level_safest_option">Safest</string>
<string name="tor_security_level_safest_description">Only allow website features required for static sites and basic services. These changes affect images, media, and scripts.</string>
</resources>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment